I am trying to understand what does spread operator do in Redux state?
I went through this question Purpose of the Spread syntax in React-Redux Reducers but wasn't convince with Answer for some reason.
Can someone please explain me in very simple terms why do we do something like this
case WHATEVER:
return {
...state,
DateSucess: action.payload,
Instead of just
case WHATEVER
return {
DataSucess: action.payload
The spread operator does the same as in ES6, is still the same behaviour (check the MDN docs).
About the motivation to use the ...state the idea is simple: Keep the old state and add or overwrite the DateSucess property.
So let's say:
const state = {foo: 'bar', DateSucess: 'oldDateSuccess', zip: 'zap'}
If you don't use spread the new value of state would be only DateSucess and you would lose the foo and zip value, using spread you are just overwriting DateSucess keeping the rest of the value untouched.
This spread would be equivalent to the following with Object.assign
return Object.assign(oldState, {DateSucess: 'newDateSucess'})
Spread operator simply return copy of array or obj associated with is. Look into example below
const abc = {
'first_key':1,
'second_key':2,
}
const newObj={...abc} //will COPY abc
const newObjWithNewKey = {...abc, 'first_key':'01 first','new_key':'007'} // will COPY abc and value of first_key will be replaces with new value as it exists. will add new keyvalue pair new_key.
console.log("abc",abc,"newObj",newObj,"newObjWithNewKey",newObjWithNewKey)
Now in redux if you just return new payload then you will lose other state values.
but if you use ... means you tell js that copy existing state and update values of specified keys if there is no key then add new one
Assume that your state structure looks like this:
const initialState = {
DateSucess: ' ',
DateFailure: ' '
}
Well then, with that state, now we write a reducer and..
The reducer is a pure function that takes the previous state and an
action, and returns the next state.
In the reducer, we make some calculation and return the next state. Keeping in mind that, we don't mutate the state. We create a copy with Object.assign().
Object.assign(state, { DateSucess: action.payload}) is also wrong: it will mutate the first argument. You must supply an empty object as the first parameter. Like this:
return Object.assign({}, state, { DateSucess: action.payload})
You can also enable the object spread operator proposal to write { ...state, ...newState } instead. In your case, it will look like:
return {...state, DateSucess: action.payload}
For more information: https://redux.js.org/basics/reducers
If you are using react and asking about react-redux, here might be the answer for you-
We shouldn't mutate a state inside the reducers. With spread operator, we make a copy of the previous state and update the copy of it. So that, we aren't mutating our prev state and at the same time, updating it. This is the function of spread operator.
Now, another question may arise. Why we can't mutate in reducers. Well that's a 'react' thing to me. React creates virtual dom for handling manipulations of doms. If you alter the internal state, the sync of dom and virtual dom may break and things will go bad.
Hope this description helps you.
Related
I just start picking up react.js so I went through a lot of tutorials and I've stumbled upon this bit which basically meant to delete an item from the state.
this is how the guy introduced to me the delete function
delTodo = id => {
this.setState({
todos: [...this.state.todos.filter(todo => todo.id !== id)]
});
};
Since I am not so familiar with javascript I had a hard time figuring out what the ... operator is doing and why exactly is he using it in the given scenario. So in order to have a better understanding of how it works, I played a bit in the console and I've realised that array = [...array]. But is that true?
Is this bit doing the same exact thing as the one from above?
delTodo = id => {
this.setState({
todos: this.state.todos.filter(todo => todo.id !== id)
});
};
Could someone more experienced clarify to me why he has chosen to be using that approach instead of the one I've come up with?
As per the documentation:
Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
reactjs.org/docs/react-component.html#state
So, in the example from the tutorial you've mentioned, you wouldn't need to make a copy of the array to update your state.
// GOOD
delTodo = id => {
this.setState({
todos: this.state.todos.filter(...)
})
}
Array.filter method creates a new array and does not mutate the original array, therefore it won't directly mutate your state. Same thing applies to methods such as Array.map or Array.concat.
If your state is an array and you're applying methods that are mutable, you should copy your array.
See more to figure out which Array methods are mutable:
doesitmutate.xyz
However, if you were to do something like the following:
// BAD
delTodo = id => {
const todos = this.state.todos
todos.splice(id, 1)
this.setState({ todos: todos })
}
Then you'd be mutating your state directly, because Array.splice changes the content of an existing array, rather than returning a new array after deleting the specific item. Therefore, you should copy your array with the spread operator.
// GOOD
delTodo = id => {
const todos = [...this.state.todos]
todos.splice(id, 1)
this.setState({ todos: todos })
}
Similarly with objects, you should apply the same technique.
// BAD
updateFoo = () => {
const foo = this.state.foo // `foo` is an object {}
foo.bar = "HelloWorld"
this.setState({ foo: foo })
}
The above directly mutates your state, so you should make a copy and then update your state.
// GOOD
updateFoo = () => {
const foo = {...this.state.foo} // `foo` is an object {}
foo.bar = "HelloWorld"
this.setState({ foo: foo })
}
Hope this helps.
Why use the spread operator at all?
The spread operator ... is often used for creating shallow copies of arrays or objects. This is especially useful when you aim to avoid mutating values, which is encouraged for different reasons. TLDR; Code with immutable values is much easier to reason about. Long answer here.
Why is the spread operator used so commonly in react?
In react, it is strongly recommended to avoid mutation of this.state and instead call this.setState(newState). Mutating state directly will not trigger a re-render, and may lead to poor UX, unexpected behavior, or even bugs. This is because it may cause the internal state to differ from the state that is being rendered.
To avoid manipulating values, it has become common practice to use the spread operator to create derivatives of objects (or arrays), without mutating the original:
// current state
let initialState = {
user: "Bastian",
activeTodo: "do nothing",
todos: ["do nothing"]
}
function addNewTodo(newTodo) {
// - first spread state, to copy over the current state and avoid mutation
// - then set the fields you wish to modify
this.setState({
...this.state,
activeTodo: newTodo,
todos: [...this.state.todos, newTodo]
})
}
// updating state like this...
addNewTodo("go for a run")
// results in the initial state to be replaced by this:
let updatedState = {
user: "Bastian",
activeTodo: "go for a run",
todos: ["do nothing", "go for a run"]
}
Why is the spread operator used in the example?
Probably to avoid accidental state mutation. While Array.filter() does not mutate the original array and is safe to use on react state, there are several other methods which do mutate the original array, and should not be used on state. For example: .push(), .pop(),.splice(). By spreading the array before calling an operation on it, you ensure that you are not mutating state. That being said, I believe the author made a typo and instead was going for this:
delTodo = id => {
this.setState({
todos: [...this.state.todos].filter(todo => todo.id !== id)
});
};
If you have a need to use one of the mutating functions, you can choose to use them with spread in the following manner, to avoid mutating state and potentially causing bugs in your application:
// here we mutate the copied array, before we set it as the new state
// note that we spread BEFORE using an array method
this.setState({
todos: [...this.state.todos].push("new todo")
});
// in this case, you can also avoid mutation alltogether:
this.setState({
todos: [...this.state.todos, "new todo"]
});
As .filter gives you a new array (than mutating the source array), it is acceptable and results in the same behaviour, making spreading redundant here.
What's not acceptable is:
const delIndex = this.state.todos.findIndex(todo => todo.id !== id);
this.state.todos.splice(delIndex, 1); // state mutation
this.setState({
todos: this.state.todos
});
slice is fine though:
const delIndex = this.state.todos.findIndex(todo => todo.id !== id);
this.setState({
todos: [
...this.state.todos.slice(0, delIndex),
...this.state.todos.slice(delIndex + 1)
]
});
If you mutate state (which keeps ref same) React may not be able to ascertain which part of your state actually changed and probably construct a tree on next render which is different from expected.
The spread operator that the guy's code is applying causes the array returned from the filter function to be copied again.
Since [.filter is returning a new array][https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter], you will already avoid mutating the array in state directly. It seems like the spread operator may be redundant.
One thing I wanted to point out too is while the values in a copied array may be the same as the values in an old array (array = [...array]), the instances change so you wouldn't be able to use '===' or '==' to check for strict equivalency.
const a = ['a', 'b']
const b = [...a]
console.log(a === b) // false
console.log(a == b) // false
console.log(a[0] === b[0]) // true
console.log(a[1] === b[1]) // true
Hope this helps!
So wanted to know if there is any way I could curry multiple assocPath methods in redux reducer. What I wish to do is update the state of an object and array in my redux reducer without mutating it. For which I have to set a value in multiple object/ arrays. The way I am trying to do it is via creating a clone of the object and updating it and then again set it to the state, this I am doing in my components state.
So currently I am do something like this in my components state. I wish to do the similar in the redux reducer
let newState;
newState = _.assocPath(["project", "value"], project, this.state);
newState = _.assocPath(["project", "error"], false, newState);
this.setState(newState);
But the problem is in redux reducer I ma not able to do it. I have to curry the methods one after the other.
Please let me know if you want more info.
Thank you
I would create the reducer using R.mergeDeepLeft with the object that I wish to merge with the original state, and then call it on the old state.
Example:
const state = { project: { value: null, error: true, other: 'other' } }
const project = {}
const reducer = R.mergeDeepLeft({
project: {
value: project,
error: false
}
})
const newState = reducer(state)
console.log(newState)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
I think the solution from Ori Drori is better than this, but this seems to me to be what you're asking for:
const foo = (project, errVal) => pipe (
prop ('state'),
assocPath (["project", "value"], project),
assocPath (["project", "error"], errVal)
)
this .setState (foo (project, false) (this))
foo (ok, so I'm terrible at naming!) accepts your project and err values and returns a function which accepts an object and returns an updated version of its state property, after setting project.value and project.error. Is this what you're looking to do?
Terminology
You say this:
But the problem is in redux reducer I am not able to do it. I have to curry the methods one after the other.
And I just want to point out some problems with the wording. First, Ramda does not have methods. Methods are functions that act on an object's state. Ramda is very careful to not mutate your state at all. Ramda instead simply has functions. R (or _ if you choose) serves as a namespace for a large number of independent functions.
And second, what you're doing has nothing to do with currying. You're simply calling the functions. My answer uses pipe to compose some functions into a new one, and perhaps that's what you meant. Currying is the process of converting a function of multiple arguments into a nested sequence of single-argument functions. (a, b, c) => 'foo' ===> (a) => (b) => (c) => 'foo'. Ramda's currying has some additional features that let you call the resulting function in more ways, but the point is that nothing in the question has anything to do with currying.
I would like to know in the React/Redux world what is the difference between this
let newState = Object.assign({}, state);
newState[action.componentDescriptor.name][action.id].Value = action.value;
return newState;
and this
return Object.assign({}, state, {
[action.componentDescriptor.name]: objectAssign({}, state[action.componentDescriptor.name], {
[action.id]: objectAssign({}, state[action.componentDescriptor.name][action.id], {
Value: action.value
})
})
});
It seems the former does not trigger a render (doesn't seem to be recognized as a state change). But the later is recognized as a state change and triggers a render. Both return new objects (state)?
In the former one, you are using Object.assign once and this creates only a shallow copy. This means your object is copied just for one level and all the nested properties point the same references. So, when you directly change it Redux can't understand the difference.
In the later one (I presume you are doing it right) you are changing your state without mutating the original one and properly. You are creating copies of your nested properties with Object.assign. This is what makes the difference.
For a cleaner version, you can use spread syntax.
return {
...state,
[action.componentDescriptor.name]: {
...state[action.componentDescriptor.name],
[action.id]: {
...state[action.componentDescriptor.name][action.id],
Value: action.value,
}
}
}
Or you can normalize your state to avoid so much nested mutations.
Currently I've a react function that removes from a Array called rents the current rent perfect. The issue is that I need to update the rent row value called status and set property from 1 to 4 the code below works. I don't seem to get how to get the Index of rent to be able to update it.
removeItem (itemIndex) {
this.state.rents.splice(itemIndex, 1) // removes the element
this.setState({rents: this.state.rents}) // sets again the array without the value to the rent prop
console.log(itemIndex) // itemIndex
}
currently I'm adding this to the code to debug but get this error
console.log(this.state.rents[itemIndex].unique_key)
Stack Trace
TypeError: Cannot read property 'unique_key' of undefined
I need to be able to update the rent property value called status from 1 to 4 and setState again
To elaborate the comments, starting first with the most important:
Like #devserkan said, you should never mutate your state (and props), otherwise you start to see some really weird hard-to-make-sense bugs. When manipulating state, always create a copy of it. You can read more here.
Now for your question:
this.setState is asynchronous, so to get your state's updated value you should use a callback function
const rents = [...this.state.rents]; // create a copy
rents.splice(itemIndex, 1);
this.setState({ rents }, () => {
console.log(this.state.rents); // this will be up-to-date
});
console.log(this.state.rents); // this won't
Personally, I like using the filter method to remove items from the state and want to give an alternative solution. As we tried to explain in the comments and #Thiago Loddi's answer, you shouldn't mutate your state like this.
For arrays, use methods like map, filter, slice, concat to create new ones according to the situation. You can also use spread syntax to shallow copy your array. Then set your state using this new one. For objects, you can use Object.assign or spread syntax again to create new ones.
A warning, spread syntax and Object.assign creates shallow copies. If you mutate a nested property of this newly created object, you will mutate the original one. Just keep in mind, for this situation you need a deep copy or you should change the object again without mutating it somehow.
Here is the alternative solution with filter.
removeItem = itemIndex => {
const newRents = this.state.rents.filter((_, index) => index !== itemIndex);
this.setState({ rents: newRents });
};
If you want to log this new state, you can use a callback to setState but personally, I like to log the state in the render method. So here is one more alternative :)
render() {
console.log( this.state.rents );
...
}
We all know the React docs say to never mutate this.state directly. I guess I have a lingering question on state arrays and immutability:
If I have an array of objects held in state, should I always use the immutability helper as in this question when mutating that array in any way?
Or is it perfectly acceptable to use [].concat() or [].slice() as answered here and here?
I ask this question again because [].concat() and [].slice() do return new arrays, but only shallow copy arrays. If I change an element of the array, it will change the array in state as well, violating the first rule of Reactjs State (I've been watching too much FMA:Brotherhood):
var arr1 = [{ name : "bill" }, { name : "chet" }];
var arr2 = arr1.slice();
// this changes both arrays
arr2[0].name = "kevin";
// check
(arr1[0].name === arr2[0].name) // => true; both are "kevin"
(arr1[0] === arr2[0]) // => true; only shallow copy
// this changes arr2 only
arr2.push({ name : "alex" });
// check again
(arr1.length === arr2.length) // => false;
(arr1[0].name === arr2[0].name) // => still true;
(arr1[0] === arr2[0]) // => still true;
I understand that the addon update is most commonly used when overriding shouldComponentUpdate, but for what I'm doing I don't need to override that function; I just need to mutate objects in the array held in state by either adding new elements to the array (solved using concat or slice), or by changing properties of existing elements (solved by using React.addons.update).
TL;DR
If not overriding shouldComponentUpdate, when should I use React.addons.update over [].slice() or [].concat() to mutate an array of objects stored in state?
You primarily need to consider boundaries. If you only use this array in the component that owns it, and optionally pass it down as props: it doesn't matter. You can modify it in any way, and unless the other components have some serious problems, they'll never know the difference because they get the new props when your component updates its state.
If you pass this array to some kind of api, such as a flux action creator or a function passed to you as a prop, then you might run into some very serious and hard to track down bugs. This is where immutability is important.
For example, in this case you might want to check some property of a person in the callback, however if it's possible for people[0].name to have changed, you have a race condition.
function savePeople(people){
$.post('/people/update', people, function(resp){
if (people[0].name === ...) {
// ...
}
});
}