reducer does not update state in react redux - javascript

I am trying update state using react-redux but the state is not being updated.
new value is coming to
"if (action.type === 'SET_LOGGED_IN')"
in reducer, but not updating the isLoggedIn as true.
What was wrong?
find the code
Login.js
function handleLoginClick(username, password, e) {
e.preventDefault();
post('/createUser', { username, password })
.then(({ status }) => {
if (status === 200) {
console.log(this.props);
this.props.setLoggedIn(true);
this.props.history.push('/');
}else{
this.props.setLoggedIn(false);
}
})
.catch(error => {
.........
})
}
..................
const mapStateToProps = state => {
return {isLoggedIn : state.reducer.isLoggedIn};};
const mapDispatchToProps = dispatch => {
return {setLoggedIn : (value) => dispatch({type: 'SET_LOGGED_IN', value: value}),}};
export default compose(
withStyles(styles),
withRouter,
connect(mapStateToProps, mapDispatchToProps)
)(NewLogin);
reducer.js
import { combineReducers } from 'redux';
import { reducer as reduxFormReducer } from 'redux-form';
const initialStates = {
isLoggedIn : false
};
const reducers = combineReducers({
reducer : (state = initialStates, action) => {
//console.log(action);
if (action.type === 'SET_LOGGED_IN') {
//console.log(action);
return {
...state,
isLoggedIn: action.value
};
}
return state;
},
form: reduxFormReducer, // mounted under "form"
});
export default reducers;

-Fixed the error-
In my code, state is updated correctly. When I accessing, I had used state.isLoggedIn which is undefined. I replaced it from state.reducer.isLoggedIn.
Now everything works charm.
Thank you #UmairFarooq , #VladimirBogomolov and all who commented and gave a try to fix it.
const mapStateToProps = state => {
return {
isLoggedIn : state.reducer.isLoggedIn
};
};

Think there might be a problem with your mapStateToProps. Try accessing your isLoggedIn state like :
const mapStateToProps = state => {
return {isLoggedIn : state.isLoggedIn};};

use componentwillupdate() to controllr when to update
return false to not update

You should be calling dispatch differently:
const mapDispatchToProps = (dispatch, val) => {
return {
setLoggedIn : (val) => dispatch({ type: 'SET_LOGGED_IN', value: val }),
}
};
Try that and it should work for you.

Related

Action.type undefined error in Redux Reducer

I'm not sure why I'm forced to do a check if actions exists in my reducer. Could it be because we are using async await in our actions / API methods?
Reducer
export const partyReducer = (state = initState, action) => {
if (action) { // <-- should not need this
switch (action.type) {
case Actions.SET_ROLES: {
const roles = formatRoles(action.roles);
return {
...state,
roles
};
}
default:
return state;
}
}
return state;
};
export default partyReducer;
Actions
import {getRoles} from '../shared/services/api';
export const Actions = {
SET_ROLES: 'SET_ROLES'
};
export const fetchRoles = () => async dispatch => {
try {
const response = await getRoles();
const roles = response.data;
dispatch({
type: Actions.SET_ROLES,
roles
});
} catch (error) {
dispatch({
type: Actions.SET_ROLES,
roles: []
});
}
};
Component that dispatches the action:
componentDidMount() {
this.props.fetchRoles();
this.onSubmit = this.onSubmit.bind(this);
}
...
export const mapDispatchToProps = dispatch => {
return {
fetchRoles: () => {
dispatch(fetchRoles());
}
};
};
The Store
import {createStore, combineReducers, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import {reducer as formReducer} from 'redux-form';
// Reducers
import partyReducer from '../reducers/party-reducer';
export default function configureStore(initialState) {
let reducer = combineReducers({
form: formReducer,
party: partyReducer
});
let enhancements = [applyMiddleware(thunk)];
if (process.env.PROD_ENV !== 'production' && typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__) {
enhancements.push(window.__REDUX_DEVTOOLS_EXTENSION__());
}
return createStore(reducer, initialState, compose(...enhancements));
}
What I've tried
I noticed my mapDispatchToProps was written kinda strange so I fixed that, but I still get the error actions is undefined if I remove the if statement :'(
import {fetchRoles as fetchRolesAction} from '../../../actions/party-actions';
...
export const mapDispatchToProps = dispatch => ({
fetchRoles: () => dispatch(fetchRolesAction())
});
Figured it out! Was my test!
it('returns expected initState', () => {
let expected = {roles: []};
let actual = partyReducer();
expect(actual).toEqual(expected);
});
^ test above is suppose to see if the initial state is return if no state is passed in. However Actions should Always be passed in.
Fix:
it('returns expected initState', () => {
let expected = {roles: []};
let actual = partyReducer(undefined, {}); // <-- undefined state, + action
expect(actual).toEqual(expected);
});

Why mapStateToProps return empty object when state has been mutated?

I create action
export const KIDS_LOAD = 'KIDS_LOAD';
export const kidsLoad = (data) => ({
type: KIDS_LOAD,
payload: data ,
});
I create reducer
import { KIDS_LOAD } from '../customActions/KidsActions';
export default (state = {}, { type, payload }) => {
if (type === KIDS_LOAD) {
return { ...state,
img: payload.img,
kidsInfo: payload.kidsInfo
};
}
return state;
}
I dispatch action
componentWillMount() {
this.props.setSidebarVisibility(true);
const { kidsLoad, record } = this.props;
kidsLoad({
img : 'https://s18670.pcdn.co/wp-content/uploads/care_about_grades_middle_school.jpg',
kidsInfo: {
kidName : 'Sasha',
kidAge : '19',
kidHandType : 'Right',
kidGender : 'Boy',
}
})
console.log( this.props.kidsTabData)
}
I map state
const mapStateToProps = state => ({
kidsTabData: state.kidsReducer,
isLoading: state.admin.loading > 0,
});
export default connect(mapStateToProps, { setSidebarVisibility, kidsLoad })(withStyles(styles)(MyLayout));
Store mutated
But console.log( this.props.kidsTabData) return empty object {}.
Can you tell where I’m wrong? It seems to me that everything goes to the store correctly, but the initialState gets to props.
The redux action kidsLoad is asynchronous.
Try the console.log in componentDidUpdate
componentDidUpdate(){
console.log(this.props.kidsTabData)
}

Cannot change redux boolean state

I feel little confused, the problem is defineAvailableTouch action and state update connected to it.
Here is my code:
Actions/index.js
import {
ANIMATE_HELLO,
HANDLE_SCROLL,
IS_TOUCH_DEVICE,
SET_ABOUT_TOP,
SET_CONTACT_TOP,
SET_PORTFOLIO_TOP
} from "../Constants/ActionTypes";
export const animateHello = hello => ({
type: ANIMATE_HELLO,
payload: hello
});
export const handleScroll = scrollDelta => ({
type: HANDLE_SCROLL,
payload: scrollDelta
});
export const defineTouchAvailable = isTouchDevice => ({
type: IS_TOUCH_DEVICE,
payload: isTouchDevice
});
export const setAboutTop = aboutTop => ({
type: SET_ABOUT_TOP,
payload: aboutTop
});
export const setContactTop = contactTop => ({
type: SET_CONTACT_TOP,
payload: contactTop
});
export const setPortfolioTop = portfolioTop => ({
type: SET_PORTFOLIO_TOP,
payload: portfolioTop
});
Reducers/index.js
import {
IS_TOUCH_DEVICE,
} from "../Constants/ActionTypes";
import { initialState } from "../Constants/InitialState/InitialState";
export const rootReducer = (state = initialState, action) => {
switch(action.type) {
case ANIMATE_HELLO:
return {
...state,
hello: action.payload
};
case HANDLE_SCROLL:
return {
...state,
scrollState: action.payload
};
case IS_TOUCH_DEVICE:
console.log(action.payload); //!!!!!! THIS PRINTS EXPECTED VALUE !!!!!!!!!!
return {
...state,
isTouchDevice: action.payload
};
case SET_ABOUT_TOP:
return {
...state,
aboutTop: action.payload
};
case SET_CONTACT_TOP:
return {
...state,
contactTop: action.payload
};
case SET_PORTFOLIO_TOP:
return {
...state,
portfolioTop: action.payload
};
default:
return state
}
};
InitialState.js
export const initialState = {
scrollState: 0,
hello: 'H',
aboutTop: 0,
portfolioTop: 0,
contactTop: 0,
isTouchDevice: true
};
App.js
import React, { Component } from 'react';
import { connect } from "react-redux";
import About from "./Containers/About";
import Contact from "./Containers/Contact";
import Page from "./Containers/Page";
import Projects from "./Containers/Projects";
import {
defineTouchAvailable,
handleScroll
} from "./Actions";
window.onbeforeunload = () => {
handleScroll(0);
document.documentElement.scrollTop = 0;
};
const mapStateToProps = state => {
return {
isTouchDevice: state.isTouchDevice
}
};
const dispatchStateToProps = dispatch => {
return {
defineTouchAvailable: isTouchDevice =>
dispatch(defineTouchAvailable(isTouchDevice)),
handleScroll: scrollState => dispatch(handleScroll(scrollState))
}
};
class App extends Component {
componentDidMount() {
try {
document.createEvent('touchevent');
this.props.defineTouchAvailable(true);
} catch(e) {
this.props.defineTouchAvailable(false);
}
console.log(this.props.isTouchDevice); //!!!!!!!!!!!!!!! THIS ALWAYS PRINTS VALUE FROM initialState !!!!!!!!!!!!!!
if(this.props.isTouchDevice) {
document.documentElement.scroll(0, 1);
}
document.addEventListener('scroll', () => {
if (document.documentElement.scrollTop === 0) {
this.props.handleScroll(0);
}
});
}
render() {
return (
<div>
<Page/>
<Projects/>
<About/>
<Contact/>
</div>
);
}
}
export default connect(mapStateToProps, dispatchStateToProps)(App);
I really can't figure out whats wrong here.
As I commented
reducer console.log prints correct value that is expected to be assigned to my state (isTouchDevice field), but
after assigning it in dispatch action nothing changes - it is always value from initialState.
Can someone please explain it to me? Do I change my redux state uncorrectly? Then why other actions work as they're expected to?
The updated value of isTouchDevice will be available in componentDidUpdate, render or componentWillReceiveProps, not in componentDidMount.
componentDidMount will only be called one time when your component is mounted.
Note: componentWillReceiveProps is deprecated, better to not use it.

Trying to populate props with async promise inside of ComponentDidMount

So inside of my uncontrolled PossibleMatches component, I know from the way React works, the initial rendering phase will occur with empty prop values (if those prop values rely on external application state (mapStateToProps)) regardless of whether or not I have a componentDidMount lifecycle method or constructor setup. In response to this, I've setup a promise inside of the componentDidMount so that when I dispatch prop functions [defaultPieces, arrangePieces], I can have the UI render an ActivityIndicator to indicate something is currently fetching. The problem is, I cannot seem to get the mapStateToProps function to understand the state when I call mapStateToProps from within the success phase of the promise. Here it is:
class PossibleMatches extends Component {
constructor(props){
super(props);
}
componentDidMount(props){
return new Promise((resolve, reject) => {
let state;
let {defaultPieces, arrangePieces, isFetching} = this.props;
let makeClothesAppear = function(){
defaultPieces();
arrangePieces();
isFetching = true;
}
resolve(makeClothesAppear());
}).then(function(state){
mapStateToProps(state);
this.props.isFetched = true
this.props.isFetching = false;
}).catch((error) => {
console.log('FetchClothesError: ', error);
})
}
}
How the UI would make a decision on what to display:
renderDecision(){
const {UpperComponents, LowerComponents} = this.props;
const {currentUpperComponent, currentLowerComponent} = this.state.currentComponent.whichPiece;
const {LowerComponentEnabled, UpperComponentEnabled} = this.state;
if (this.props.isFetching){
return (<div className='activityLoader'>
<ActivityIndicator number={3} duration={200} activeColor="#fff" borderWidth={2} borderColor="50%" diameter={20}/>
</div>);
} else if (this.props.isFetched){
return (<div className = "PossibleMatches_Container">
<i className = 'captureOutfit' onClick = {this.snapshotMatch}></i>
{UpperComponents.map((component) => {
return (<UpperComponent key={component.createdAt} id={component.id}
switchComponent={this.switchFocus}
setCurrentPiece = {this.setNewPiece}
evaluatePiece={this.isOppositeComponentSuggested}
image={component.image}
toggleToPiece = {(LowerComponentEnabled) => {if (LowerComponentEnabled === false){this.setState({LowerComponentEnabled: true})}else{return;} this.setState({currentLowerComponent: this.props.suggestedBottoms[0]})}}
isLowerComponentEnabled={LowerComponentEnabled}
ref={this.residingUpperComponent}
className = {this.state.currentComponent.whichPiece.whichType === 'match' ? 'PossibleMatches_Container' : this.state.currentComponent.whichPiece.whichType === 'bottom' ? 'standalonePiece' : 'standalonePiece'}/>)
})
}
{LowerComponents.map((component) => {
return (<LowerComponent key={component.createdAt} id={component.id}
setCurrentPiece = {this.setNewPiece}
evaluatePiece={this.isOppositeComponentSuggested}
image={component.image}
toggleToPiece={(UpperComponentEnabled) => {if (UpperComponentEnabled === false){this.setState({UpperComponentEnabled: true})}else{return;} this.setState({currentUpperComponent: this.props.suggestedTops[0]})}}
switchComponent={this.switchFocus}
isUpperComponentEnabled={UpperComponentEnabled}
ref={this.residingLowerComponent}
className = {this.state.currentComponent.whichPiece.whichType === 'match' ? 'PossibleMatches_Container' : this.state.currentComponent.whichPiece.whichType === 'bottom' ? 'standalonePiece' : 'standalonePiece'}/>)
})
}
</div>)
}
}
render(){
return(
<div className = 'GorClothingContainer'>
{/*<Wardrobe upperComponent={this.state.currentComponent.whichPiece.currentUpperComponent} lowerComponent={this.state.currentComponent.whichPiece.currentLowerComponent} enableCapture={(snapshot) => this.snapshotMatch = snapshot} />*/}
{this.renderDecision()}
</div>
);
}
My PossibleMatches Reducer
import {INITIAL_PIECES, GET_ANCILLARY_PIECES, ORGANIZE_PIECES, SET_CONTEMPLATED_PIECE} from '../actions/types';
const initialState = {
UpperComponents: [],
LowerComponents: [],
contemplated_piece: null,
extraTops: [],
extraBottoms: [],
standaloneTops: [],
standaloneBottoms: [],
suggestedTops: [],
suggestedBottoms: []
}
export default function(state = initialState, action){
switch(action.type){
case INITIAL_PIECES:
return Object.assign({}, state, {contemplated_piece: action.payload.contemplated_piece},
{extraTops: action.payload.extra_tops},
{extraBottoms: action.payload.extra_bottoms},
{standaloneTops: action.payload.standalone_tops},
{standaloneBottoms: action.payload.standalone_bottoms},
{suggestedTops: action.payload.suggested_tops},
{suggestedBottoms: action.payload.suggested_bottoms})
case GET_ANCILLARY_PIECES:
return Object.assign({}, state, {extraTops: action.payload.extra_tops},
{extraBottoms: action.payload.extra_bottoms},
{standaloneTops: action.payload.standalone_tops},
{standaloneBottoms: action.payload.standalone_bottoms},
{suggestedTops: action.payload.suggested_tops},
{suggestedBottoms: action.payload.suggested_bottoms})
case ORGANIZE_PIECES:
return Object.assign({}, state, {UpperComponents: action.payload.UpperComponents},
{LowerComponents: action.payload.LowerComponents})
case SET_CONTEMPLATED_PIECE:
return Object.assign({}, state, {contemplated_piece: action.payload.contemplated_piece})
default:
return state;
}
}
My combineReducers segment
import {combineReducers} from 'redux';
const allReducers = combineReducers({
Playlist: PlaylistReducer,
eventOptions: eventTicketReducer,
possibleMatches: PossibleMatchesReducer,
Intro: combineForms({
basicUserInfo: BasicUserInfoState,
GenderInfo: GenderInfoState,
ContactInfo: ContactInfoState
}, 'Intro'),
routing: routerReducer,
form: formReducer
});
Prop Values:
PossibleMatches.defaultProps = {
isFetching: true,
isFetched: false
}
My mapStateToProps function
function mapStateToProps(state){
return {UpperComponents: state.possibleMatches.UpperComponents,
LowerComponents: state.possibleMatches.LowerComponents,
contemplatedPiece: state.possibleMatches.contemplated_piece,
extraTops: state.possibleMatches.extraTops,
extraBottoms: state.possibleMatches.extraBottoms,
standaloneTops: state.possibleMatches.standaloneTops,
standaloneBottoms: state.possibleMatches.standaloneBottoms,
suggestedTops: state.possibleMatches.suggestedTops,
suggestedBottoms: state.possibleMatches.suggestedBottoms}
}
function mapDispatchToProps(dispatch){
return {
defaultPieces: () => {
dispatch(defaultPieces())
},
arrangePieces: () => {
dispatch(arrangePieces())
},
getCorrespondingPieces: () => {
dispatch(getCorrespondingPieces())
},
setEvaluatedPiece: () => {
dispatch(setEvaluatedPiece())
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(PossibleMatches)
My Question is: What exactly is wrong with the way that I've implemented the promise. With the reducers and the redux actions setup correctly(I know because I've logged the fetched items to the console from the redux actions file), how can I properly populate the prop values in mapStateToProps. Currently the error is:
Im using React 16.4.0
A simple redux use case would seem as follows
possibleMatches.jsx (Component file)
class PossibleMatches extends React.Component {
state = {
isFetching: false
}
componentDidMount() {
this.setState({isFetching: true})
fetchingSomethingFromServer()
.then(resp => {
this.setState({isFetching: false})
this.props.UpdateRedux(resp)
});
}
render() {
const { isFetching } = this.state;
const { data } = this.props;
return (
isFetching ? <div>loading...</div> : <div>{data}</div>
)
}
}
export default connect(state => ({ data: state.possibleMatches.data }), {UpdateRedux})
actions.js (action creator file)
Use this action to update any data into redux
export const UpdateRedux = (data) => {type: 'UPDATE_REDUX', payload: data}
reducers.js
This is the file that holds the redux state
const defaultState = {
data: null
}
export default (state = defaultState, action) => {
switch(action.type) {
case 'UPDATE_REDUX':
return {data: action.payload};
default:
return state
}
}
In your combine reducers import this reducer and assign it as follows
import possibleMatches from 'reducers.js';
combineReducers({ possibleMatches });

ReactJS x Redux: Reducer not returning state values

Hi I'm new at React and Redux.
I'm met with a problem with the reducer while trying to fetch a user object from the database. But it seems like it is not returning the state to the correct place?
On my front end editProfile.js:
import { a_fetchUser } from '../../../actions/resident/actions_user';
class EditProfile extends Component {
componentDidMount() {
this.props.fetchProfile({ iduser: this.props.auth.user.iduser });
console.log(this.props.store.get('isProcessing')); // returns false
console.log(this.props.store.get('retrievedUser')); // returns empty object {} when it's supposed to return data
}
// code simplified...
const mapStateToProps = state => ({
store: state.r_fetch_user,
auth: state.authReducer
});
const mapDispatchToProps = (dispatch, store) => ({
fetchProfile: (user) => {
dispatch(a_fetchUser(user));
}
});
export const EditProfileContainer = connect(
mapStateToProps,
mapDispatchToProps,
)(EditProfile);
}
Action actions_user.js:
import axios from 'axios';
const startFetchUser = () => ({
type: 'START_FETCH_USER',
});
const endFetchUser = response => ({
type: 'END_FETCH_USER',
response,
});
export const a_fetchUser = (user) => (dispatch) => {
dispatch(startFetchUser());
return axios.post('/rdb/getUser/', user)
.then((res) => {
console.log(res);
dispatch(endFetchUser(res));
})
.catch((err) => {
console.log(err);
dispatch(endFetchUser({ status: 'error' }));
});
};
Reducer userReducer.js:
import Immutable from 'immutable';
export const fetchUserState = Immutable.Map({
isProcessing: false,
feedbackType: null,
feedbackMsg: null,
retrievedUser: {},
});
export const r_fetch_user = (state = fetchUserState, action) => {
switch (action.type) {
case 'START_FETCH_USER':
console.log('start'); // printed
return state.set('isProcessing', true);
case 'END_FETCH_USER':
if (action.response.data.status === 'success') {
console.log(action.response.data.data[0]); // data retrieved from database successfully
return state.set('isProcessing', false).set('retrievedUser', action.response.data.data[0]);
} else {
return state.set('isProcessing', false).set('retrievedUser', {});
}
default:
return state;
}
};
My aim is to retrieve the object retrievedUser from the store. I've tried to console.log(this.props.store) on the front end and it did return a Map of the initial state, fetchUserState.
I've also tried to state.set (without returning) and it was successful so I came to a conclusion that there was something wrong with the return statement?
Additional details:
Using MERN stack.
this looks wrong:
const mapDispatchToProps = (dispatch, store) => ({
fetchProfile: (user) => {
dispatch(a_fetchUser(user));
}
});
What you need to do is to use bindActionCreators with, you can see example here and here:
function mapDispatchToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch)
}
or you can also change the syntax to:
const mapDispatchToProps = (dispatch) => ({
fetchProfile: a_fetchUser(user);
});
I am not sure what exactly your state.set() method does (in reducer) but if its mutating the state, then your reducer will not remain PURE function since its changing the original state obj. So please update below reducer method to start returning new state obj which should not mutate existing state obj:
export const r_fetch_user = (state = fetchUserState, action) => {
switch (action.type) {
case 'START_FETCH_USER':
console.log('start'); // printed
return state.set('isProcessing', true);
case 'END_FETCH_USER':
if (action.response.data.status === 'success') {
console.log(action.response.data.data[0]); // data retrieved from database successfully
return state.set('isProcessing', false).set('retrievedUser', action.response.data.data[0]);
} else {
return state.set('isProcessing', false).set('retrievedUser', {});
}
default:
return state;
}
};

Categories

Resources