Reputation: 3239
When using Redux, I have my initial state:
const initialState = {
foo: {
bar: {
foobar: 1,
barfoo: 2
}
},
xyz: true,
abc: {
jkl: [1,2,3,4]
}
}
Then I have the reducer, and inside a switch. Suppose I have a case X in which I want to change xyz to false.
Is this enough?
return {
...state,
xyz: false
}
or should I do?
return {
foo: {
bar: {
...state.foo.bar
}
},
xyz: false,
abc: {
jkl: [...state.abc.jkl]
}
}
Upvotes: 1
Views: 119
Reputation: 76929
Dan's answer gives you a practical and working syntax (do that! it's cleaner), but let's understand why.
Each spread copy is shallow, not deep. Only the first level is duplicated. For example, let's say you attempt to clone an object this way:
> let original = { someNumber: 1, someArray: [1, 2] }
> let copy = { ...original }
The objects original
and copy
are distinct. If you set properties, they won't reflect each other.
> x2.someNumber = 2
> x2.newProperty = "hello"
> console.log(x1)
{ someNumber: 1, someArray: [1,2] } // same someNumber, no newProperty!
But the value of each individual key is not duplicated, it's just a reference to the original. The someArray
property references the very same array instance in both objects. So:
> console.log(x1.someArray)
[1, 2]
> x2.someArray.push(3)
> console.log(x1.someArray)
[1, 2, 3]
Both original.someArray
and copy.someArray
are referencing the same array instance. Two references in two objects, but only one underlying array.
There's no easy, 100% foolproof way to actually clone an object, because not all objects are simple JSON-like dictionaries. But you have some options in this other answer.
When working with React and Redux, many problems can be avoided by using a library like ImmutableJS, which ensures each object is distinct and every modification produces a different object. The performance is good and the syntax more comfortable in many cases, as well.
Upvotes: 0
Reputation: 3831
No need to do your second implementation! That would create a catastrophe of reducers lol.
Just use:
return {
...state,
xyz: false
}
Not only is this the way that is advised in Redux apps, imagine having to debug reducers where you use your second implementation, and you make typos.
On the case of nested properties, you would need to do something like this:
return {
...state,
foo: {
bar: { ...state.foo.bar, roflWaffle: 3 }
}
}
Also, recommend checking out other answers here too, very helpful in depth information that will greatly help you understand how JavaScript and Redux works under the hood.
Upvotes: 1
Reputation: 18901
It is enough if you are aware that the spread operator only does a shallow clone:
const a = {x: {y: 10}};
const b = {...a};
b.x.y = 42;
console.log(b.x.y); //=> 42
console.log(a.x.y); //=> 42
I understand that with Redux you want to return new state. Just know that this potentially opens up the gate to unwanted mutations and side effects.
With this simple example, I have shown how a simple reducer can potentially tamper with the history of your Redux states.
Upvotes: 1