Why does state show update before setState is called - javascript

handleOptions is a method which is called every time a button is clicked and is passed an options object which it uses to update this.state.options.
this.state = {
options: []
}
handleOptions(options){
let stat = this.state.options;
console.log(stat)
stat.push(options)
this.setState({ options: stat })
}
As you can see I'm logging the previous state options before calling this.setState to add it to the state. But for some reason the log output shows the updated state. It logs an empty array the first time as expected but after that it logs the updated state
I also tried passing a function to this.setState instead of an object, same result.
handleOptions(options){
this.setState((prevState) => {
let stat = prevState.options;
console.log(stat)
stat.push(options)
return {options: stat}
})
}
What am I missing???

push mutates original array. You could use concat instead.
handleOptions(options){
this.setState((prevState) => {
let stat = prevState.options;
console.log(stat)
return {options: stat.concat(options)}
})
}
As of
But for some reason the log output shows the updated state
Console tries to show you current object state. So if you modify an array using push you will see modified values. If you want to log the value at the very specific moment you could say log a copy console.log(stat.slice()) or a stringified representation console.log(JSON.stringify(stat))

Related

Array type variable changes to string on setState

How can I get the state and array to work consistently?
The results are so confusing and inconsistent.
This is state:
this.state = {
tutorSubjects: [],
};
This is setting state; the first console.log logs the subject in an array, the second logs and empty array:
const name = target.name;
if (name === "tutorSubjects") {
let subjects = this.state.tutorSubjects.concat(value);
console.log(subjects)
this.setState({
tutorSubjects: subjects
}, console.log(this.state.tutorSubjects))
}
this handles submit and logs th subject as a string without the array:
handleSubmit = e => {
console.log(this.state.tutorSubjects)
}
My main goal is to send an array to the server, and now it's sending a string.
I have not idea why, but now that I have changed the state to subjects and updated state like below, It works perfectly.
if (name === "tutorSubjects") {
let subjects = this.state.subjects.concat(value);
console.log(subjects)
this.setState({
subjects
}, console.log(this.state.subjects))
}
The callback argument to setState should be a callback function but you're not supplying it that way, instead you're evaluating console.log(...) immediately and displaying that before setState gets called.
Fix it by ensuring it runs after:
this.setState({ subjects }, () => console.log(this.state.subjects));
Your original code is roughly equivalent to:
let v = console.log(this.state.subjects);
this.setState({ subjects }, v);
Where clearly that's out of order when expressed that way.

why it is not showing update value on button click from state?

could you please tell me how to get updated value from state.here is my code
https://codesandbox.io/s/cool-ives-0t3yk
my initial state
const initialState = {
userDetail: {}
};
I enter 10 digit number on input field and press enter and update the user detail like this
const onSubmit = async values => {
if (values.mobile.length === 10) {
setUserDetail({ msdin: values.mobile });
console.log(userDetail);
}
};
setUserDetail({ msdin: values.mobile }); here I am updating my userdetail .
and try to console the update value like this
console.log(userDetail); .it is showing currently undefined.but expected output is {msdin:'9999999999'} (or whatever it is type in input field)
The problem is that you are using hooks and it's not synchronised, it's async. Therefore, accessing the detail immediately after setting the value will not be possible. If you want to access the data there, you will have to use values.mobile
The state will keep the last value until the next render is called.
You can see this information on react-hooks document
During subsequent re-renders, the first value returned by useState will always be the most recent state after applying updates.
So, the code should look like:
const onSubmit = async values => {
if (values.mobile.length === 10) {
const newUserDetailState = { msdin: values.mobile }
setUserDetail(newUserDetailState);
// do your stuffs with the newUserDetailState instead of userDetail
console.log(newUserDetailState);
}
};
The state setter setUserDetail is async, that means that the new state value won't be available immediately.
To see if the state update use useEffect like this :
useEffect(() => {
console.log('useEffect -> UserDetail : ', userDetail);
}, [userDetail]);

Filter an Object Array and set to state Array variable

This is the JSON Input that I am going to filter by orderId and then set to the state variable.
[{"orderId":3,"userId":3,"firstName":"Amal","lastName":"kankanige","contactNo":"011-3456787","status":"pending","deliveryAddress":"No:24/c,Anders Road,Wijerama,Wijerama,Srilanka","productName":"Apple","quantity":2,"total":100.0,"id":null,"order_date":"2019-01-31 10:28:29"}]
this is my state.
this.state = {
merchentOrders: [],
filterMerchentOrders: []
}
//this is the method
when I am filtering by orderId it's successfully working and then I want to set the filtered output to " this.setState({filterMerchentOrders: filterMerchentOrdersX});". when I console out the state as below the values are not set.
getOrderDetails = id => {
console.log("id ==" ,id);
let filterMerchentOrdersX = this.state.merchentOrders.filter(value => {
return (
value.orderId.toString()
.indexOf(id) !== -1
);
});
this.setState({filterMerchentOrders: filterMerchentOrdersX});
//this is working
console.log("filterMerchentOrdersX ==" ,filterMerchentOrdersX);
//this gives an empty array
console.log("this.state.filterMerchentOrders ==" ,this.state.filterMerchentOrders);
};
It might be the reason that setState runs asynchronusly, when you console.log the value state might not be updated.
To view the state after it updates, you can pass a callback function.
this.setState({filterMerchentOrders: filterMerchentOrdersX},()=>{
console.log("this.state.filterMerchentOrders ==" ,this.state.filterMerchentOrders);
})
you've written console.log() immediately after setState method, since setState is an async method an at the time java script runs your console.log it will be showing you the previous state in which obviously array is not set, for this you should put your console in the second argument of setState that is a callback function.
this.setState({[event.target.name]: event.target.files[0]},()=>console.log(this.state));
or you can simply user async await.
Since you're using the ES6 syntax, your function could be shorter and neater. But it seems to me that you're not doing anything wrong
getOrderDetails = id => {
const filterMerchentOrders = this.state.merchentOrders.filter(
value => value.orderId.toString().indexOf(id) !== -1
);
this.setState({filterMerchentOrders});
}

react-native update object using setState

Is it possible to update object using setState? I tried this code:
import moment from 'moment';
constructor() {
super();
this.state = {
pressedDate:{
},
}
}
updateState = (day) => {
let updateDay = moment(day.dateString).format(_format);
//When I console.log updateDay, I get this '2019-06-30'
console.log(updateDay,'updateDay')
//I want to update pressedDate with updateDay which is '2019-06-30'
this.setState({
pressedDate:{
updateDay : {dots: [vacation, massage, workout], selected: true}
}
})
}
I am trying to update my object with updateDay but nothing happens when I run this code.
You've said "nothing happens," but that code clearly replaces this.state.pressedDate with a new object with just the updateDay property.
If you meant to add/update updateDay without completely replacing pressedDate (that is, if it has other properties), you'd do that by making a copy and then updating the copy. The idiomatic way to do that uses property spread:
this.setState(({pressedDate}) => ({
pressedDate: {
...pressedDate,
updateDay : {dots: [vacation, massage, workout], selected: true}
}
}));
Two key things to note there:
It's using the callback version of setState. Because state updates are asynchronous, you must use the callback version whenever setting state (an updated pressedDate) based on existing state (the rest of pressedDate other than updateDay).
...pressedDate spreads out the properties of the current version into the new object being created. Then we add updateDay, which will overwrite any previous one.

State not being set corretly

I have a model Component in my ReactJs project, where I have a picture being, show and I want to pass the data of the picture, that a user clicked on, it can neither be raw data or a URL.
I have made a handle, that can both delete the picture (if pressed with the Ctrl key), or just open up the modal if clicked normally
showModalSpeceficHandler = (event, image) =>{
let index = this.state.images.indexOf(image)
if(event.ctrlKey){
let temp = this.state.images.slice(); //makes a shallow copy of the array
temp.splice(index, 1); //remove item
this.setState(prevState => ({ images: temp }));
}else{
console.log(image); // logs the data of the clicked image
this.setState(
state => ({imageModalData: image}),
() => this.showModalHandler()
);
console.log(this.state.imageModalData) //logs the data of the last added image
}
}
so the issue now is as mentioned in the comments, that the state is not set correctly. I was suspecting that the showModalHandler would change the state but
it simply sets the state, if it should be shown or not:
showModalHandler = () =>{
this.setState({showModal: !this.state.showModal})
}
What is happening, or overwriting the state, since it is not being set correctly
setState is an asynchronous operation.
When your setState call needs to refer to the old state you should use the alternative setState signature where you pass a function as first argument:
setState((state) => ({ showModal: !state.showModal }));
See https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
This is not technically a callback. The callback argument is the second setState parameter which is rarely used (so, more or less you should never use it).
See https://reactjs.org/docs/react-component.html#setstate
try to bind your showModalHandler function to this in your constructor like this :
constructor(props){
super(props)
/* your state*/
this.showModalHandler = this.showModalHandler.bind(this)
}

Categories