Reputation: 345
I thought assign
was supposed to make a new object, that's why I did this in my reducer:
case types.ADD_ATTRIBUTE:
var newState = Object.assign({}, state)
newState.attributes[action.industry].push(action.attribute)
return Object.assign({}, state, newState);
case types.REMOVE_ATTRIBUTE:
var newState = Object.assign({}, state)
var removeIndex = newState.attributes[action.industry].indexOf(action.attribute)
newState.attributes[action.industry].splice(removeIndex, 1)
return Object.assign({}, state, newState);
However, when I do this, the component will not trigger an update (componentWillReceiveProps
). It does receive the new props, but the react-redux
internal shouldComponentUpdate
does not detect changes.
What am I doing wrong here?
Upvotes: 3
Views: 1949
Reputation: 4201
Here's how you could handle the types.ADD_ATTRIBUTE
case:
With Object.assign:
const newActionIndustry = state.attributes[action.industry].concat(action.attribute)
const newAttributes = Object.assign({}, state.attributes, {
[action.industry]: newActionIndustry
})
const newState = Object.assign({}, state, {
attributes: newAttributes
})
Handle the types.REMOVE_ATTRIBUTE
case by yourself using this code.
Upvotes: 0
Reputation: 1913
React-redux's shouldComponentUpdate()
does a shallow comparison of the state in order to decide to render or not. This shallow comparison checks only one level depth of the object, it means that if you don't change the reference of the state itself or any of its first-level properties, it won't trigger an update of the component.
Your array is deeply nested in state.attributes[action.industry]
, and your action is not modifying state nor attributes, so react-redux won't update your component. In order to solve your problem, you need to change attributes[action.industry]
, creating a new array (e.g. using Array.concat()
instead of Array.push()
or using the spread operator like in attributes[action.industry] = [...attributes[action.industry], action.attribute ]
Alternatively, if you are using a stateful component you can create your own version of shouldComponentUpdate()
that takes into account the attributes property in order to decide to render or not.
Upvotes: 0
Reputation: 345
I eventually settled on this: (with some ES6 magic)
case types.ADD_ATTRIBUTE:
let newAttrState = state.attributes[action.industry].slice()
newAttrState.push(action.attribute)
return Object.assign({}, state, { attributes: { [action.industry]: newAttrState }} );
case types.REMOVE_ATTRIBUTE:
var newAttrState = state.attributes[action.userIndustry].slice()
let removeIndex = newAttrState.indexOf(action.attribute)
newAttrState.splice(removeIndex, 1)
return Object.assign({}, state, { attributes: { [action.userIndustry]: newAttrState }} );
*Update: Which I now realize, is overriding the whole attributes
object, with only one dynamically keyed array, while I need to sustain the other arrays stored in that object...
Upvotes: 0
Reputation: 184
In case if you want to re-render object containing attributes[action.industry]
, you need to recreate this array same as you do with state.
case types.ADD_ATTRIBUTE:
return {
...state,
attributes: {
...state.attributes,
[action.industry]: [...state.attributes[action.industry], action.attribute]
}
}
case types.REMOVE_ATTRIBUTE:
const removeIndex = newState.attributes[action.industry].indexOf(action.attribute)
return {
...state,
attributes: {
...state.attributes,
[action.industry]: [
...state.attributes[action.industry].slice(0, removeIndex),
...state.attributes[action.industry].slice(removeIndex + 1)
]
}
}
Upvotes: 4