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 } };
}
Related
I build an app in React with Redux and I try to send to my state an object and I try to save it in 'thisUser' but I don't know how to write that 'return' because mine doesn't work.
My Redux state:
const initialState = {
thisUser: {}
}
export function usersReducer(state = initialState, action) {
switch (action.type) {
case 'users/addUser':
return { ...state, thisUser: { ...state.thisUser, ...action.payload} } //the problem
default:
return state
}
}
Dispatch method:
dispatch({ type: "users/addUser", payload: new_user });
Can you tell me how to write that return, please?
If you want to append new user then why are you using object type. You should use Array Type thisUser.
const initialState = {
thisUser: []
}
export function usersReducer(state = initialState, action) {
switch (action.type) {
case 'users/addUser':
return { ...state, thisUser: [ ...state.thisUser,action.payload ] }
default:
return state
}
}
Or
If you want to save only single user object then change only that line in your code:
return { ...state, thisUser: action.payload }
It's better to use an array type for if you have a list of users .
If you have a case when you need to use an object just change the brackets [ ] on my code to curly braces { } .
const initialState = {
thisUser: [],
}
export function usersReducer(state = initialState, action) {
switch (action.type) {
case 'users/addUser':
return { ...state, thisUser: [ ...state.thisUser, ...action.payload]}
default:
return state
}
}
I'm going to add an object to the array, the second time I want to add another object the whole array becomes number one and I end up with an error, my goal is to add a task to program Todo with Redux.
I also get this errors:
TypeError: Cannot read property 'length' of undefined
TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))
//todoReducer.js
import {ADD_TODO} from '../Actions/Todo';
const initialState = {
todos:[],
};
const handleAddTodo = (state, action) => {
const {todos} = state;
const newTodo =[...todos, {
id: todos.length + 1,
text: action.title,
isComplete: false,
}]
return (
todos.push(newTodo)
)
}
export default function todoRDS(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
return handleAddTodo(state, action)
default:
return state
}
}
Change your return function you return wrong value. You need to return the state
const handleAddTodo = (state, action) => {
const {todos} = state;
return {
...state,
todos: [...todos, {
id: todos.length + 1,
text: action.title,
isComplete: false,
}]
}
}
export default function todoRDS(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
return {...state, todos: [...state.todos, { id: state.todos.length +1, title: action.title, isComplete: false }] }
default:
return state
}
}
state is unmutable in react and redux you need to create a new state with old state values and add your new todo inside that new object. If you still want to use handeAddTodo try this:
const handleAddTodo = (state, action) => {
return {...state, todos: [...state.todos, { id: state.todos.length +1, title: action.title, isComplete: false }] }
}
I have the following state object in redux:
console.log({
jobOffers: {
filters: {
employments: [],
careerLevels: [],
jobTypeProfiles: [],
cities: [],
countries: [],
searchTerm: '',
currentPage: 1,
pageSize: 5
}
}
});
I want to set the array employments new.
That's my redux reducer:
export const reducer = (state = initialStateData, action) => {
switch (action.type) {
case Action.SET_ARR_FILTER:
{
const newNestedState = {
...state[action.key],
[action.key]: action.value,
};
return { ...state,
[action.key]: newNestedState
};
}
default:
return state;
}
};
The action:
export const SET_ARR_FILTER = 'SET_ARR_FILTER';
export const setEmployment = employment => ({
type: SET_ARR_FILTER,
key: 'employments',
value: employment,
});
But my object looks like this after the reducer has been called:
console.log({
employments: {
employments: ['HelloWorld']
},
})
What is wrong here ?
You're a level too deep (or not deep enough, depending on how you see it).
You need something like:
case Action.SET_ARR_FILTER:
{
const { filters } = state
return { ...state,
filters: {
...filters,
[action.key]: action.value
}
};
}
Similar to Mark's answer, all one line if you like.
export const reducer = (state = initialStateData, action) => {
switch (action.type) {
case Action.SET_ARR_FILTER:
return {
...state,
filter: {
...state.filter,
[action.key]: action.value
}
}
default:
return state;
}
};
Finally got it myself. Answer is:
case Action.SET_ARR_FILTER:
{
return {
...state,
jobOffers: {
...state.jobOffers,
filters: { ...state.jobOffers.filters,
[action.key]: action.value
},
},
};
}
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.
Is this reducer OK:
function someReducer(state = initialState, action) {
if (action.type === SOME_ACTION) {
const newState = Object.assign( {}, state );
// ...
// doing whatever I want with newState
// ...
return newState;
}
return state;
}
and if is OK, why we need all those immutable libraries to complicate our lives.
p.s
Just trying to comprehend Redux and immutability
export default function (state = initialState, action) {
const actions = {
SOME_ACTION: () => {
return {
...state
}
},
ANOTHER_ACTION: () => {
return {
...state
error: action.error
}
},
DEFAULT: () => state;
}
return actions[action.type] ? actions[action.type]() : actions.DEFAULT();
}
I prefer doing this instead. I am not a big fan of switch statements.
The standard approach is to use a switch/case with spread syntax (...) in your reducer.
export default function (state = initialState, action) {
switch (action.type) {
case constants.SOME_ACTION:
return {
...state,
newProperty: action.newProperty
};
case constants.ERROR_ACTION:
return {
...state,
error: action.error
};
case constants.MORE_DEEP_ACTION:
return {
...state,
users: {
...state.users,
user1: action.users.user1
}
};
default:
return {
...state
}
}
}
You can then use ES6 spread syntax to return your old state with whatever new properties you want changed/added to it.
You can read more about this approach here...
https://redux.js.org/recipes/using-object-spread-operator
I found something that I really like:
import createReducer from 'redux-starter-kit';
const someReducer = createReducer( initialState, {
SOME_ACTION: (state) => { /* doing whatever I want with this local State */ },
SOME_ANOTHER_ACTION: (state) => { /* doing whatever I want with local State */ },
THIRD_ACTION: (state, action) => { ... },
});
If your state has nested objects or arrays, Object.assign or ... will copy references to your older state variable and it may cause some issue. This is the reason why some developers use immutable libraries as in most of the case state has deep nested array or objects.
function someReducer(state = initialState, action) {
if (action.type === SOME_ACTION) {
const newState = Object.assign( {}, state );
// newState can still have references to your older state values if they are array or orobjects
return newState;
}
return state;
}