can any one please explain me react setState new to me - reactjs

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.

Related

Adding to a state array without the resulting array being read only, in React Native?

Im programming a component where I am mapping over a component using an array I have stored in state.
const [animalList, setList] = useState(['cat', 'dog'])
{ animalList.map((tag) => {
return (
<AnimalButton animalz={tag}/>
)
})
}
I want to add to the state array to force to make the app rerender. I attempted to do so with a .push function, store the increased array in a temporary variable and assign that new array to the state, as push() and unsplice don't return the actual array.
onSubmit={(values, actions) => {
actions.resetForm();
animalList.push(values.pet)
let newList = animalList
setList(animalList = newList)
}}
However I get this error [TypeError: "animalList" is read-only]?
Is there a way to change add to animalList without my resulting list in the state becoming read only?
Yes you cannot push this into const.
However, you can use this approach.
setList([...animalList,values.pet])

how does a onChange handler get access to the previous state of variables?

i found following example somewhere and I cannot figure out how it is supposed to work, can someone explain it please, I understand the usage of setState and initializing it, but I don't understand the variable prevInputData! what is calling the setInputData and how the previous state is being passed as parameter when setInputData is being called, i'm so confused!
//state stuff
const [inputData, setInputData] = useState({firstName: "", lastName: ""})
//onChange handler
function handleChange(event) {
const {name, value} = event.target
setInputData(prevInputData => { //HOW THIS IS SUPPOSED TO WORK?
return {
...prevInputData,
[name]: value
}
});
}
//form input
<input name="lastName"
value={inputData.lastName}
onChange={handleChange}
/>
This is kind of misleading. It's not actually the state value from the previous render cycle, it's the current state value being passed as an argument to the functional state update callback. By the time the callback is invoked though, during reconciliation, to update state, it is now the state from the last render cycle, and returns the next state value.
Functional Updates
setInputData(prevInputData => { // actually current state when queued
return {
...prevInputData,
[name]: value
}
});
Input data was set at some point right ? Like say at the first run the values are set to be firstname:"jhon" and lastname:"doe" now the next time you run setState it already has reference of the previous value because it was set before.
setInputData(prevInputData => { //Get the previous values which were set
return {
...prevInputData, //uses spread operator to add those values here so that they are not lost
[name]: value //setting new values.
}
});
In other words if you dont use prevInputData which is just a variable name it can be anything, your previous changes will be overridden. Also by run i mean the current state it holds, not like you could come back after a refresh and it'll be there. its like updating for input boxes, with each input box you leave, the state is updated and retains the previous input box values you added, if you dont do ...prevData then the previous values you entered will be lost.

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

data is not coming outside the function in reactjs

How to bring the pushed array data outside the function,Every thing is
working fine in click function.But when Im trying to print this outside of
the function console.log(this.state.selectedSchedule[0].storeName) , It is
throwing the error:
Cannot read property 'storeName' of undefined.
Code:
this.state = {
selectedSchedule:[]
};
click(e){
console.log('e',e);
document.getElementById("detailView").style.display="block";
this.state.selectedSchedule=[];
for(let i=0;i<this.scheduless.length;i++){
if(this.scheduless[i].scheduleId === e){
this.state.selectedSchedule.push(this.scheduless[i]);
break;
}
}
console.log('cheudkl',this.state.selectedSchedule);
this.setState=({
selectedSchedule:this.state.selectedSchedule
});
console.log(this.state.selectedSchedule[0]);
console.log(this.state.selectedSchedule[0].storeName) //printing
};
//html render
render(){
console.log(this.state.selectedSchedule[0].storeName) // here it's throwing me the error(not printing)
}
Reason is, initial value of this.state.selectedSchedule is [] and during initial rendering you are trying to get the storeName of 0th items, and clear at that time 0th time doesn't exist so
this.state.selectedSchedule[0] ===> undefined
And you are trying to get the value from undefined that's why it is throwing the error. Simply put the check on length before printing the values.
Like this:
render(){
// now it will print the values once some item will be added
if(this.state.selectedSchedule.length)
console.log(this.state.selectedSchedule[0].storeName);
return (....)
}
Another issue is in the way you are updating the state value.
Changes:
1- Never mutate the state value directly, Treat this.state as if it were immutable.
2- setState is a function so we need to call it this.setState({}).
write it like this:
click(e){
document.getElementById("detailView").style.display="block";
let arr = [];
arr = this.scheduless.filter(el => el.scheduleId === e);
this.setState({
selectedSchedule: arr
});
};
You should not mutate the state, push is mutating the array, and you are re assigning the state object in the click function this.state = ....
Instead, use the builtin setState method of react to update it:
this.setState({key: value});
As for arrays you can add items with the ES6 spread operator without mutating it:
const nextState = [...this.state.selectedSchedule, newValue];
this.setState({selectedSchedule: nextState });
EDIT
As Mayank Shukla mentioned, in addition to this solution you should check to see if the length of your array is bigger then 0:
this.state.selectedSchedule.length > 0 && console.log(this.state.selectedSchedule[0].storeName);
In the first render call this array is empty hence its length is 0.
This is not correct way of updating the state
this.setState=({
selectedSchedule:this.state.selectedSchedule
});
You need to edit this as :
if(this.scheduless[i].scheduleId === e){
this.setState({selectedSchedule:[...this.state.selectedSchedule, this.scheduless[i]]});
break;
}
You need to set the state only then the render function will be called again and your changes to the state variable will be visible.
You should never mutate the state (changing the contents of this.state) directly.
That is, instead of assigning an empty array to part of the state and then push every item, create a new array:
const selectedSchedule = this.scheduless.filter(schedule => schedule.scheduleId === e)
this.setState({selectedSchedule});

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