I am trying to add an element to an array in the state
and change another array element's property. Say we have the following state
structure:
{
menuItems: [{
href: '/',
active: true
}]
}
After dispatching the ADD_MENU_ITEM
action I want to end up with this state
:
{
menuItems: [{
href: '/new',
active: true
}, {
href: '/',
active: false,
}]
}
I have tried managing this in Redux reducers in several fashions:
function reducer(state = {}, action) {
switch (action.type) {
case ADD_MENU_ITEM: {
let menuItems = state.menuItems;
let newMenuItem = action.newMenuItem;
// First try
menuItems[0].active = false;
menuItems.unshift(newMenuItem);
state = Object.assign({}, state, { menuItems: menuItems });
// Second try
menuItems[0].active = false;
menuItems.unshift(newMenuItem);
state = Object.assign({}, state, {menuItems: Object.assign([], menuItems)});
// Third try
menuItems[0].active = false;
state = (Object.assign({}, state, {
menuItems: [
Object.assign({}, newMenuItem),
...menuItems
]
}));
// Fourth try
menuItems[0].active = false;
state = update(state, {
menuItems: {$unshift: new Array(newMenuItem)}
});
console.log(state);
return state;
}
}
}
In the fourth try, I am using React's Immutability Helpers but it never works. I logged the state to the console before returning the state and it logs correctly, but when logging inside the components which get re-rendered
, the menuItems array does not add the first item, although the active
member is set to false
.
What could I be doing wrong?
dispatch(action) Dispatches an action. This is the only way to trigger a state change. The store's reducing function will be called with the current getState() result and the given action synchronously.
Redux is a JavaScript library that depicts how an application's state should be managed and accessed. It supports state management via a centralized store. Though there are many libraries like Flux that support state management, data flow in Redux is clear and easier to integrate.
Reducers are the only way to change states in Redux. It is the only place where you can write logic and calculations. Reducer function will accept the previous state of app and action being dispatched, calculate the next state and returns the new object.
The way you have it written, the state won't update unless you explicitly update it using setState() (most likely in the componentWillReceiveProps() method). When you use mapStateToProps() with the Redux connect() HOC, you are mapping your Redux state to your component through its props, so in your case this. props.
The state in the reducer should be immutable and for this reason should not be modified. It is also recommended to flatten your object whenever possible.
In your scenario your initial state could be an array as such:
[{
href: '/',
active: true
}]
In your reducer, try returning a brand new array as follows:
function reducer(state = {}, action) {
switch (action.type) {
case ADD_MENU_ITEM: {
return [
action.newMenuItem,
...state.map(item => Object.assign({}, item, { active: false }))
];
}
}
}
More information about reducers can be found here: Redux Reducers Documentation
Helpful excerpt from the documentation:
It’s very important that the reducer stays pure. Things you should never do inside a reducer:
- Mutate its arguments;
- Perform side effects like API calls and routing transitions;
- Calling non-pure functions, e.g. Date.now() or Math.random().
MORE INFO ADDED
In your reducer, and for all four tries, you are modifying the existing state before returning it.
This results in react-redux
, when checking if your state has changed, not to see any changes as both the previous and next states are pointing to the same object.
Here are the lines I am referring to:
First Try:
// This line modifies the existing state.
state = Object.assign({}, state, { menuItems: menuItems });
Second Try:
// This line modifies the existing state.
state = Object.assign({}, state, {menuItems: Object.assign([], menuItems)});
Third Try:
// This line modifies the existing state.
state = (Object.assign({}, state, {
menuItems: [
Object.assign({}, newMenuItem),
...menuItems
]
}));
Fourth Try:
// This line modifies the existing state.
state = update(state, {
menuItems: {$unshift: new Array(newMenuItem)}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With