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
Related
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
}
}));
I am trying to make multiple state variable names from data in an array that is already a state variable. The number of variables needed can change from page load to page load so I can't really initialize them right away
I've set up a function below that I believe should do what I intend for it to, but I can't figure out where to place it in the component.
makeButtons() {
this.state.Buttons.forEach((button) => {
let key = button.fields.Label
this.setState({ [key]: '' })
});
}
I've tried calling it in render(), but that just gives me a infinite loop error, which makes sense. Any ideas?
EDIT: Tried calling in componentDidMount() but that doesn't seem to work either. Not sure what the issue would be there. Code below
base('ButtonInfo').select({view: 'Grid view' }).eachPage(
(Buttons, fetchNextPage) => {
this.setState({
Buttons
});
fetchNextPage();
}
);
this.state.Buttons.forEach((button) => {
let key = button.fields.Label
this.setState({ [key]: '' })
});
EDIT: Answer is in the comments below, I needed to add await before the api call and make componentDidMount() async.
How about doing it in componentDidMount()? This method is called only once as the after the app is rendered for the first time, and is often used to initialize the app with some data.
can anyone lemme know the working of each line as i dont have any idea
can we have let in set sate? please explain these 2 lines
const userDetails = previousState.userDetails
return { userDetails: {...userDetails, [key]: value} }
the return in set state i have not understand
this.setState((previousState) => {
const userDetails = previousState.userDetails
return { userDetails: {...userDetails, [key]: value} }
})
setState can accept a function from the previous state to a new state:
this.setState(previousState => newState);
previousState => newState is an arrow function, that will accept the previous state (which will be provided by setState when it is invoked), and has to provide the new state.
In the code you posted, the new state is calculated by taking an object from the previous state called userDetails, and then appending a new key-value pair to it.
This is done using two mechanisms. The first, is the spread operator: ...userDetails which adds to the new userDetails value all of the key-value pairs that were in the original object. Next, the new value is added:
[key]: value
This means that a new pair is to be created, where the key is the value of the key variable (I suspect this is available somewhere in the function before the setState call), and the value is equal to a variable named value, which again is previously available.
This is the notation to use when you want to use a dynamic value as a key in a map.
Edit - Answering question from comment
Any function body that is more than one line needs to be put in a block { ... }. When you want to return a value from that function, a return keyword is used.
The return you see in your setState is the return from the function that is passed to setState.
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)
I am trying to add an array to a state that contains an array. I wish to add the data to the end of the array. My data looks like this:
getInitialState: function() {
return {name: [{program: "File Manager"},
{program: "Add File"},
{program: "Index"},
{program: "File Editor"}],
program: "Index",
display: true};
},
and I wish to update the name variable to also contain something. For example add {program: "Text Editor"}
How could I go about doing this with this.setState()? Or, if not, is there another way to do this. I read about $push but couldn't really find any examples that helped me out. I wish to do this in componentDidMount:
Create a new array and assign it to the state:
var currentName = this.state.name;
var newName = currentName.concat([{program: "Text Editor"}]);
this.setState({name: newName});
(The first line isn't necessary, just included for clarity.)
You could also use the function version of setState:
this.setState(function(state) {
return { name: state.name.concat([{program: "Text Editor"}]) };
});
If you're in an environment that supports ES2015 arrows, you can do this in a pretty concise way:
this.setState(state => ({ name: state.name.concat([{program: "Text Editor"}]) });
Since concat already generates a new array, you don't need to use the immutability helpers, but if you want to, it'd look like this:
var newState = update(this.state, { // generate a new state object...
name: { $push: [ "TextEditor" ] } // with the item pushed onto the `name` key
});
It's possible to mutate an existing property of this.state and then re-set it with setState, e.g.
// warning: antipattern!
this.state.name.push("Text Editor");
this.setState({name: this.state.name});
but this is not considered a very good practice (you should always avoid directly modifying this.state):
Notes:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
You can read the this.state.name array, modifying it (by pushing to the array), then call setState again.