arrow function use in setstate - reactjs

I wrote a piece of code that I don't quite remember how it works, embarrassing to say.
The state is as below:
state={
api:[
{
id: 1,
purpose: "For the Android phone",
link: "playgoogle/GooglePlay",
status: "1"
},
{
id: 2,
purpose: "For the Android phone",
link: "playgoogle/GooglePlay",
status: "0"
}
]
}
And this is the snippet of code that I don't quite understand.
activateAPI=(id)=>{
this.setState(prev => ({
api: prev.api.map(api => api.id === id ? { ...api, status: 1 } : api)
}))
this.changeStatus(id,1);
}
I understand that a value is passed into the activateAPI as id but I don't quite understand how the prev and api: prev.api works here. I understand that the api inside the map is like a foreach where it checks whether the current item's id in the json array matches the function id then what does {...api,status:1} mean?
Any clarification would be much appreciated. Thank you for reading.

The nature of setState is async. What you've written is pretty clear that when you call setState with its callback signature. Internally once the callback completes, React returns the callback to setState. And here's what you're doing,
(state, props) => stateChange
Above is the true signature of the setState that React provides.
State is a reference to the component state at the time the change is being applied.
For more info link
Also when you use spread operator, it spreads the object and updates the prop that is changed. So when you do {...api, status:1}. It will compare the API object from the initial one and updates the status value to 1.
Remember this will be the shallow comparison. For updating nested state objects you need to go to the nested level to observe proper state update.

api: prev.api.map(api => api.id === id ? { ...api, status: 1 } : api)
That mean , When mapping array if it find the api with the id matched, it with return the new api object with property 'status' change to '1' . If not it will just return the old api object
sorry for my bad english :D hope it help

What the Spread operator does is that it creates a new object.
So in this case when you are doing {...api, status: 1} a new object is getting created with all the keys present in api object and then overriding the status value with 1 when api.id === id is true.
let x = {key: "Key", val: 1, val2: 22}
//spread and update the value, it'll only update `val` all other values wil be as is
console.log({...x, val: 2})
//the original object will be as is
console.log(x)
let y = {z: {key: "Key", val: 3}, z1: 2}
//assign y to y1 by spreading
let y1 = {...y}
y1.z.val = 4;
//z.val in both y and y1 will be modified because spread will not spread deep objects
console.log(y1)
console.log(y)
Coming to the arrow function in setState will accept a function as first parameter. React will pass state and props as params to the passed function. This way you will be able to access the state in order to update it by using old state.
Below is the setState function
setState(updater, [callback])
The first argument is an updater function with the signature:
(state, props) => stateChange
state is a reference to the component state at the time the change is being applied. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input from state and props.
Hope this helps.

Related

conditional operator to setState

I have a button for pagination in my React component in render function. Can you help me to convert this conditional operator to setState? I have a lot of this yellow bugs in console trough conditional operator. I need to use it but I don't know how to use setState with it.
<button onClick={() => this.makeHttpRequestWithPage(this.state.current_page < this.state.total_pages ? this.state.current_page + 1 : this.state.current_page = this.state.total_pages)}>»</button>
Do not mutate state directly. Use setState()
I think you have misunderstood the concept of "immutability" in JavaScript.
Basically, if i have an immutable JS object, lets say state is:
state = {
name: "Dimitar",
age: 18,
hobbies: ["Programming", "Watching TV shows", "Studying"]
}
I cant directly mutate it this way:
state.name = "Something Else"
The object itself can be reassigned, but its values are set (constant). To get around this, React has created the setState function which basically makes a copy of the previous state object, reassigns new values to it and then overwrites the previous object.
So if you want to mutate the state, or alter its contents then you need to use the setState in such fashion:
setState({
name: "Dimitar2"
})
Here, you can also pass the prevState as a parameter and use it, but i will let you do the research on that. Also, you can use the spread operator etc.
<button onClick={() =>
this.setState({current_page: this.state.current_page < this.state.total_pages ? this.state.current_page + 1 : this.state.current_page}, () => this.makeHttpRequestWithPage(this.state.currentPage);)}>»</button>
Here you first update the current page based on the condition and after state is set you make the call.

setState override existing state

I have the following function to set state:
const [study, setStudy] = useState(defaultState)
const setValue = (section, key, value) => {
setStudy({...study, [section]: {...study[section], [key]: value}})
}
But for some reason it keeps overriding the existing state, what am I doing wrong?
I call the function like this:
setValue('company', 'name', 'test')
setValue('property', 'state', 'test2')
setValue('property', 'address', 'test3')
Structure of data:
const defaultState: StudyData = {
client: {},
company: {},
property: {},
study: {}
}
I think the problem is that the study value you destruct is still an old version (a memoized one). It is the same between all setValue calls and thus your first two setValue calls do essentially nothing. If this is what's happening to you, the best way to fix it would be to give a callback function to setStudy. Any function given to a setter will get the absolute latest version of the state as parameter. Try it like this:
setStudy((latestStudy) => {
return {
...latestStudy,
[section]: {...latestStudy[section], [key]: value}
}
});
When dealing with multiple state-updating events, the standard behavior for React is to batch them. The state-updating method does not immediately update the state of the component, React just puts the update in queue to be processed later when event handling is complete. Changes are all flushed together at the end of the event and you don't see the intermediate state.
With batching, when the final set-state event executes, it has no recollection that the prevState has been updated (asynchrounous), which is why the final state appears to only reflect changes made by the last event.
Using a call-back function as the first argument for your setStudy method will help you return the intermediate states and avoid the issue of batching. The updater function will loop through and shallowly merge all updated states with the previous component state. This ensures that we receive all state-updates and use all intermediate states before ultimately arriving at the final update.
const setValue = (section, key, value) => {
setStudy(study => {
return {
...study,
[section]: { ...study[section], [key]: value }
};
});
};
Also see working sandbox: https://codesandbox.io/s/delicate-tree-vqrz7

Is it ok to modify part of prevState inside setState?

Is there a real disadvantage to modifying part of prevState and returning that part inside setState() ?
Example:
this.setState(prevState => {
prevState.myObject.isItTrue = !prevState.myObject.isItTrue;
return {myObject: prevState.myObject};
});
Rather than:
this.setState(prevState => {
const myObject = Object.assign({}, prevState.myObject);
myObject.isItTrue = !myObject.isItTrue;
return {myObject: myObject};
});
Is there any real disadvantage to the first code where I save myself the Object.assign() ?
EDIT: If I am correct, prevState.myObject is simply a reference to this.state.myObject, so changing prevState.myObject actually changes this.myObject.object as well! However, this doesn't seem to break anything, as long as I use setState() to pass an object that contains the new data, even if it's just a reference to the old objects inside this.state.
Do you agree that this is still ok, i.e. it won't break anything to do it like this?
Following documentation:
state is a reference to the component state at the time the change is being applied. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input from state and props.
https://reactjs.org/docs/react-component.html
So you should not apply changes directly to that state.
Either way, why not do something like this?:
this.setState(prevState => ({
myObject : {
...prevState.myObject,
isItTrue: !prevState.myObject.isItTrue,
}
}));
This way will get all the elements from the prevState but also change all the ones you want to modify.
First prevState and the this.state are the same object. So you are modifying the actual state directly.
Secondly, down the road you might pass the myObject as a prop to another component, and since it will be the same object always that component will not know that something has changed and it will not re-render (for example PureComponent and ones that implement componentDidUpdate or shouldComponentUpdate and test for changes)
See https://codesandbox.io/s/zen-aryabhata-m07l4 for showcases of all issues.
So you should use
this.setState(state => ({
myObject: {
...state.myObject,
isItTrue: !state.myObject.isItTrue
}
}));

is this actually mutating state? A bad practice?? - react

this is a piece of code I came across and I am not sure what it is doing actually?
it feels to me like it is mutating the state
Version 1:
this.state.items = [...this.state.initialItems];
Now, is the above code mutating state??
And can the above also be written as Version 2:
this.setState({
items:[...this.state.initialItems];
});
the thing is that version 1 is working but version 2 isnt.
So,what's wrong here?
what am I missing?
what does ...this.state.initialItems do? is that the spread operator?
This code is definitely mutating state. Don't do it:
this.state.items = [...this.state.initialItems];
Yes, the ... is a spread operator in this context.
For version 2, make sure you don't use the semicolon.
It should just be :
this.setState({
items:[...this.state.initialItems]
});
And of course like the poster below has said, it's asynchronous so you won't see the results right away.
Augmentation 1:
If you want to do things after your state is ready, you can add a callback as your 2nd argument to setState.
this.setState({
items:[...this.state.initialItems]
}, ()=>{
// do stuff
}
);
Augmentation 2:
Your first argument to setState can be a function as well, as the other poster has mentioned, so that when you access state it will be the "freshest" copy, and not the one that was captured when you created the object that you passed to setstate:
this.setState((stateNOW) => {
return {items:[...stateNOW.initialItems]};
}, ()=>{
// do stuff
}
);
Version 1 is wrong. But the real question is Why?
state is a special object in React and it is not to be mutated.
Say you declare an object:
const obj = {
label: 'doe'
value: 'foo'
}
Then, you update the obj with
obj.value = 'bar'
This is the process of mutation and should never perform it with a state variable. Instead we use setState as a request to change the state.
... is the spread syntax.
As the other answers have mentioned, the reason why you think it is not working might be because state updates are asynchronous.
You have to use the setState callback or componentDidUpdate lifecycle hook to log the value of state.
setState(stateChange[, callback])
Another issue in the code is the use of this.state.something inside setState. Like we already mentioned before setState is asynchronous and a number of state updates may be merged. So using this.state.initialItems may not always work out for you, if you miss a update.
this.setState((prevState) => {
return {items: [...prevState.initialItems]};
});
This would be the right way to use a value of previous state.
Version 1 is mutating your state and this is a bad practice.
Version 2 is not and the reason why it's not working (actually it is) is that calls to setState are asynchronous and therefore you will not have access to its up-to-date value in the next line.
Should you need access to the new state value, you can use:
this.setState(prevState => ({
items: [...prevState.initialItems],
}), () => {
console.log(this.state) // new state value
})
Please also refer to this link in ReactJS docs about how to set state properly.
The ... is indeed the spread syntax.
You can use it, for example:
To add items to an array
const arr = [1, 2, 3, 4]
const newArr = [...arr, 5, 6] // This will spread everything in arr and add numbers 5 and 6 to newArr
console.log('arr', arr)
console.log('newArr', newArr)
To get items from an array
const arr = [1,2,3,4]
const [firstItem, secondItem, ...others] = arr
console.log('firstItem:', firstItem)
console.log('secondItem:', secondItem)
console.log('others:', others)
To create a new object
const obj = {
name: 'Stack Overflow',
age: 10,
}
const newObj = {
...obj, // gets all properties from obj
age: 12, // but only change age
}
console.log('obj', obj)
console.log('newObj', newObj)

React set state variable

I've an confusion. I'm trying to add array variable in setState. My code is working properly but wanted to confirm some doubt before committing my code.
Which is right way to store array in state variable ?
var names = ['Jake', 'Jon', 'Thruster'];
this.setState({
state: names
});
Or
this.setState((state) => {
state.items.push(names[0]);
return state;
});
What is necessary of return statement here ?
Can some one please explain me the difference here ? I searched in google but I'm still confused.
var names = ['Jake', 'Jon', 'Thruster'];
this.setState({
names //according to Airbnb rules
});
or
this.setState({
names: names
});
this.state.names = ['Jake', 'Jon', 'Thruster'];
setState takes a second argument - callback, that will called after setting State properties
setState({property1: value1, ...}, () => { //some code after State changed })
The first approach is much more common and in my opinion, easier to read. The issue with your code is you don't need to use the key state, because the function you are calling is "setting state". The key should be something like firstNames.
this.setState({
firstNames: names
});
You could also just pass the object in the function like this because setState takes an object as a parameter.
var namesObject = {
firstNames: names
}
this.setState(namesObject);
I would read more about it here and keep doing small tutorials on this an you'll get the hang of it.
https://facebook.github.io/react/docs/react-component.html#setstate

Resources