So I have a reducer which doesn't seem to be updating the state at all whenever I called the 'LOGIN' action. It could be a problem with my React Redux code. It's either my component is not getting re rendered whenever the store's state changes or the reducer is not changing the stores state at all.
Reducer ------
const initialState = {
messages: [],
loginDetails: {
email: '',
password: ''
},
activeUsers: [],
loginActive: false
}
const messageReducer = (state = initialState, action) => {
switch(action.type) {
case 'ADD_MESSAGE':
if(state.messages.length < 50) {
let newStateMessages = [...state.messages]
newStateMessages.unshift(action.payload);
console.log(newStateMessages);
return {...state, messages: newStateMessages};
} else {
let newStateMessages = [...state.messages]
newStateMessages.pop();
newStateMessages.unshift(action.payload);
return {...state, newStateMessages};
}
case 'LOGIN':
console.log('LOGIN');
console.log(action);
const newLoginDetails = {
email: action.payload.email,
password: action.payload.password
};
console.log({...state, loginDetails: newLoginDetails});
return {...state, loginDetails: newLoginDetails};
case 'UPDATE_USERS':
const newActiveUsers = action.payload;
return {...state, activeUsers: newActiveUsers};
case 'LOGIN_ACTIVE':
return {...state, loginActive: true};
case 'LOGIN_EXIT':
return {...state, loginActive: false};
default:
return state;
}
}
export const store = createStore(messageReducer);
React Redux connect -----
const mapStateToProps = state => {
return { ...state }
}
export default connect(mapStateToProps)(Home);
This mapStateToProps returns...
{
activeUsers: []
dispatch: ƒ dispatch(action)
loginActive: true
loginDetails: {email: "", password: ""}
messages: []
__proto__: Object
}
when it should return...
{
activeUsers: []
loginActive: true
loginDetails: {email: "example#gmail.com", password:
"password"}
messages: []
__proto__: Object
}
I have tested for sure that the dispatch to the reducer is getting called, and the payload is correct. However, the reducer is failing to update the state with the LOGIN action type.
Can you try this:
const mapStateToProps = ({activeUsers,loginActive,loginDetails,messages}) => ({
activeUsers,
loginActive,
loginDetails,
messages
})
Related
I call get_user_profile_api inside the app after login. On successful login my app has user and token in the state. But as soon as GET_USER_PROFILE_SUCCESS is called, the state is left only with userData and isLoading. user and token are removed from the state.
import { CONST } from '../../utils/Const';
import * as types from './types';
const initialState = {
isLoading: false,
user: null,
userData: null,
isLoggedIn: false,
token: '',
};
export default authReducer = (state = initialState, action) => {
console.log('action :: ', action);
switch (action.type) {
case types.GET_USER_PROFILE_LOADING:
case types.LOGIN_LOADING:
return {
isLoading: true,
};
case types.GET_USER_PROFILE_SUCCESS:
return {
...state,
isLoading: false,
userData: action.payload,
};
case types.LOGIN_SUCCESS:
return {
...state,
isLoading: false,
isLoggedIn: true,
user: action.payload.user,
token: action.payload.token,
};
case types.LOGIN_ERROR:
case types.GET_USER_PROFILE_ERROR:
return {
...state,
isLoading: false,
};
default:
return {
...state,
isLoading: false,
};
}
};
there is nothing wrong with this code I think the issue is where you are calling these actions from and you can call them inside a use-effect hook to get a user profile if there is a token or maybe you are not mapping state to props correctly
I was not writing ...state in the LOADING case. That's why my state was being removed.
How do I update the property of an object in redux and preserve the rest of the object..
const appReducer = (state = initialState, action) => {
switch (action.type) {
case 'LOGIN_SUCCESS':
return Object.assign({}, state, {
user: { loggedIn: true, level: 'default' },
});
case 'UPDATE_PACKAGE': {
// add a new value for the user.level which would be in action.level
return { ...state, level: action.level };
}
default:
return state;
}
};
So I would expect the UPDATE_PACKAGE to change the contents of the redux store level property... but its unchanged...
So it seems like you're setting level on the root state instead of the user object.
This should fix it:
case "UPDATE_PACKAGE": {
return { ...state, user: { ...state.user, level: action.level } };
}
Disclaimer: this question is targeting specific package reduxsauce
Takes classic redux action, by dispatching a single action, it will flow thru all the reducer and if we want to update the state, we catch the type in each and every reducer as we see fit
loginPage.js
this.props.memberLogin({ name: 'john' }); //{ type: MEMBER_LOGIN, payload: { name: 'john' } }
LoginAction.js
const memberLogin = member => {
return { type: MEMBER_LOGIN, payload: member }
}
authReducer.js
const INITIAL_STATE = { isLoggedIn: false }
switch(state = INITIAL_STATE, action) {
case MEMBER_LOGIN: return { ...state, isLoggedIn: true };
default: return state;
}
memberReducer.js
const INITIAL_STATE = { member: null }
switch(state = INITIAL_STATE, action) {
case MEMBER_LOGIN: return { ...state, member: action.payload };
default: return state;
}
Wondering by using reduxsauce, can we still achieve something similar as demonstrated above? (dispatch single action and update both reducer)
Yes, you can.
I created this Snack example to help you, but the gist is that you have to configure your reducers to listen to the same action.
Kinda like so:
const reduceA = (state, action) => ({
...state,
a: action.value,
});
const reduceB = (state, action) => ({
...state,
b: action.value,
});
const { Types, Creators: actionCreators } = createActions({
testAction: ['value'],
});
const HANDLERS_A = {
[Types.TEST_ACTION]: reduceA,
};
const HANDLERS_B = {
[Types.TEST_ACTION]: reduceB,
};
In the example both reducers A and B have their state values altered by the same action testAction.
For practice with Redux, I have in index.js
const state = [
{
resort: 'Jay Peak',
date: '2018-2-22',
powder: true,
backcountry: false,
},
];
const action = {
type: constants.ADD_DAY,
payload: {
resort: 'Mad River Glen',
date: '2018-3-14',
powder: true,
backcountry: false,
},
};
const nextState = allSkiDays(state, action);
console.log(`
initial state: ${JSON.stringify(state)}
action: ${JSON.stringify(action)}
new state: ${JSON.stringify(nextState)}
`);
and my reducers for composition,
export const skiDay = (state = [], action) => {
action.type === constants.ADD_DAY ? action.payload : state;
};
export const allSkiDays = (state = [], action) => {
switch (action.type) {
case constants.ADD_DAY:
return [...state, skiDay(null, action)]; // COMPOSITION!!!! use skiDay()
default:
return state;
}
};
and I keep getting this result,
initial state: [{"resort":"Jay Peak","date":"2018-2-22","powder":true,"backcountry":false}]
action: {"type":"ADD_DAY","payload":{"resort":"Mad River Glen","date":"2018-3-14","powder":true,"backcountry":false}}
new state: [{"resort":"Jay Peak","date":"2018-2-22","powder":true,"backcountry":false},null]
I've tried many things why is null still being spread onto the array and not the new object?
This reducer is not returning the next state.
export const skiDay = (state = [], action) => {
action.type === constants.ADD_DAY ? action.payload : state;
};
Do this instead:
export const skiDay = (state = [], action) => {
return action.type === constants.ADD_DAY ? action.payload : state;
};
import {UPDATE_USER} from '../actions/index';
const DEFAULT_STATE = {
createdAt:"",
name:"",
email:"",
password:"",
skill:"",
goal:"",
step1:"",
step2:"",
step3:"",
step4:"",
step5:"",
posts:[],
completed:0
}
export default function(state = DEFAULT_STATE, action) {
if (action.error) {
action.type = 'HANDLE_ERROR'; // change the type
}
switch (action.type) {
case UPDATE_USER:
console.log(action.payload)
return {
createdAt:action.payload.createdAt,
name:action.payload.name,
email:action.payload.email,
password:action.payload.password,
goal:action.payload.goal,
skill:action.payload.skill,
step1:action.payload.step1,
step2:action.payload.step2,
step3:action.payload.step3,
step4:action.payload.step4,
step5:action.payload.step5,
completed:action.payload.completed,
}
React is not detecting a prop change. I'm pretty sure the answer lies with me mutating reducer arguments(from researching the question). Does anyone know how I would restructure to not mutate?
edit -my react class snippet is below. My map dispatch to props is at bottom. A user logs in to app gets redirected to this page where I set local state of page from redux in componentwillMount(). Then I have a function that calls api and updates redux. React is supposed to see this change because props have changed? Or do I have to set state manually within a componentWillRecieveProps()?
class YourPage extends React.Component {
constructor(props) {
super(props);
this.state = {
post:"",
date:"",
email:"",
completed:0,
posted:true,
timeSincePost:"",
lastPost:""
}
this.handleInputChange = this.handleInputChange.bind(this);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
}
const mapStateToProps = (state) =>({
name:state.user.name,
email:state.user.email,
completed:state.user.completed,
})
const mapDispatchToProps = (dispatch) => ({
callApi: (value, state) => {
var obj = {
date:moment.tz(moment.tz.guess()).format(),
post:state.post,
email:state.email,
completed:(parseFloat(state.completed) + .75),
}
API.addPost(obj)
.then(function(res){
dispatch(updateUser(res.data))
})
}
})
export default connect(mapStateToProps,mapDispatchToProps)(YourPage);
Use Object.assign
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
import { UPDATE_USER } from '../actions/index';
const DEFAULT_STATE = {
createdAt: "",
name: "",
email: "",
password: "",
skill: "",
goal: "",
step1: "",
step2: "",
step3: "",
step4: "",
step5: "",
posts: [],
completed: 0
}
export default function (state = DEFAULT_STATE, action) {
if (action.error) {
action.type = 'HANDLE_ERROR'; // change the type
}
switch (action.type) {
case UPDATE_USER:
console.log(action.payload)
return Object.assign({}, state, {
createdAt: action.payload.createdAt,
name: action.payload.name,
email: action.payload.email,
password: action.payload.password,
goal: action.payload.goal,
skill: action.payload.skill,
step1: action.payload.step1,
step2: action.payload.step2,
step3: action.payload.step3,
step4: action.payload.step4,
step5: action.payload.step5,
completed: action.payload.completed,
});