I'm reading Forms section of reactjs documentation and just tried this code to demonstrate onChange usage (JSBIN).
var React= require('react');
var ControlledForm= React.createClass({
getInitialState: function() {
return {
value: "initial value"
};
},
handleChange: function(event) {
console.log(this.state.value);
this.setState({value: event.target.value});
console.log(this.state.value);
},
render: function() {
return (
<input type="text" value={this.state.value} onChange={this.handleChange}/>
);
}
});
React.render(
<ControlledForm/>,
document.getElementById('mount')
);
When I update the <input/> value in the browser, the second console.log inside the handleChange callback prints the same value as the first console.log, Why I can't see the result of this.setState({value: event.target.value}) in the scope of handleChange callback?
From React's documentation:
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value. There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
If you want a function to be executed after the state change occurs, pass it in as a callback.
this.setState({value: event.target.value}, function () {
console.log(this.state.value);
});
As mentioned in the React documentation, there is no guarantee of setState being fired synchronously, so your console.log may return the state prior to it updating.
Michael Parker mentions passing a callback within the setState. Another way to handle the logic after state change is via the componentDidUpdate lifecycle method, which is the method recommended in React docs.
Generally we recommend using componentDidUpdate() for such logic instead.
This is particularly useful when there may be successive setStates fired, and you would like to fire the same function after every state change. Rather than adding a callback to each setState, you could place the function inside of the componentDidUpdate, with specific logic inside if necessary.
// example
componentDidUpdate(prevProps, prevState) {
if (this.state.value > prevState.value) {
this.foo();
}
}
You could try using ES7 async/await. For instance using your example:
handleChange: async function(event) {
console.log(this.state.value);
await this.setState({value: event.target.value});
console.log(this.state.value);
}
Watch out the react lifecycle methods!
http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
https://reactjs.org/docs/react-component.html
I worked for several hours to find out that getDerivedStateFromProps will be called after every setState().
😂
Sometime this issue occurs with state.
In case of hooks, you should use useEffect hook, As below-
const [fruit, setFruit] = useState('');
setFruit('Apple');
useEffect(() => {
console.log('Fruit', fruit);
}, [fruit])
This saved my day, Hope will help you!!!
Accessing this.state after calling the setState method is not guaranteed to return the updated status due to the asynchronous nature of setState.
To guarantee an update after calling setState, there are two solutions you may pursue.
Solution 1: As mentioned in one of the above answers, put your code in the componentDidUpdate method
Solution 2: As mentioned in another of the above answers, pass your stuff as a callback
this.setState({value: myValue}, function () {
this.functionThatIsExecutedWhenStateIsUpdated();
});
It's important to note that these two solutions are not clearly interchangeable. The one cannot easily solve all the use-cases of the other. As a general rule, if you can, best practice says that solution 1 is preferred. But, there are use-cases where only solution 2 "more effectively" works such as the "update-my-view-and-post-my-data" use case. This use case goes like this:
After adding an item, say, "Add Schedule", I want to both add that item to a front-end list and immediately post the just-updated-list to the backend, as demonstrated in the concept below:
If you dont do either solution, i.e. if you only say this in your code:
addToItemArray = () => {
this.setState{{ scheduledItemsArray: newObjectListWithMax}}
this.postData();
}
<button className="btn btn-secondary btn-block" onClick={this.addToItemArray}>Add Shedule</button>
... you will post the list excluding the "Delivery to Max" item, because the state wont be updated when you this.postData() (again, because its asynchronous).
If you utilise solution 1, you would make a POST after typing in every character in the Schedule Name textbox!
There are other ways aswell to cater for this use-case but solution 2 best conveys the intent when reading the code.
Given the ubiquitous nature of this use case in virtually every web app, the callback technique explained by Michael's answer is an indispensable piece of code in every developers toolkit.
async-await syntax works perfectly for something like the following...
changeStateFunction = () => {
// Some Worker..
this.setState((prevState) => ({
year: funcHandleYear(),
month: funcHandleMonth()
}));
goNextMonth = async () => {
await this.changeStateFunction();
const history = createBrowserHistory();
history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}
goPrevMonth = async () => {
await this.changeStateFunction();
const history = createBrowserHistory();
history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}
React bathces different set state calls so that it can determine what is the most optimal strategy for rerendering the website is going to be.
Imagine you have an application where you have a lot of different components. Perhaps, with one button click you are updating the state in multiple components, not just on the current one. In this case, React does not want to just completely isolate and do all those different updates independently.
React wants to figure out if it can stack all these updates together, maybe there is a more optimal way of updating these components so that it is more performant. This is what React is doing behind the scenes. As a result, set state call is asynchronous call.
Simply putting - this.setState({data: value}) is asynchronous in
nature that means it moves out of the Call Stack and only comes back
to the Call Stack unless it is resolved.
Please read about Event Loop to have a clear picture about Asynchronous nature in JS and why it takes time to update -
https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4
Hence -
this.setState({data:value});
console.log(this.state.data); // will give undefined or unupdated value
as it takes time to update.
To achieve the above process -
this.setState({data:value},function () {
console.log(this.state.data);
});
Related
In the article (https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html) from react docs, there is the following:
Refs can be useful in certain cases like this one, but generally we recommend you use them sparingly. Even in the demo, this imperative method is nonideal because two renders will occur instead of one..
here is a relevant part of the demo from https://codesandbox.io/s/l70krvpykl?file=/index.js:
handleChange = index => {
this.setState({ selectedIndex: index }, () => {
const selectedAccount = this.props.accounts[index];
this.inputRef.current.resetEmailForNewUser(selectedAccount.email);
});
};
i tweaked this demo, and i figured out a way to re-render just once like this (i delete the callback function because it runs after componentDidUpdate so it causes re-render twice):
handleChange = index => {
this.setState({ selectedIndex: index }); // set state of Parent Component (itself)
const selectedAccount = this.props.accounts[index];
this.inputRef.current.resetEmailForNewUser(selectedAccount.email); // set State of Child Component
};
i am wondering why this works and re-render just once. i thought maybe react batch both setState.
i am not sure why it's not recommended to do it like this ?
The use of the setState callback "guarantees" that this call back will only be executed after the state variables within the {} are updated:
this.setState({.... }, () => {to do only after state variables are updated})
In the second case you mentioned, there is no guarantee that this "sequential" execution will be respected. In this case you have no problems with parallelism respect of objects affected by the index parameter. However, if there are other components that depend on the "selectedIndex" state variable you could get into estrange problems depending a lot on how you structured them.
I'm reading Forms section of reactjs documentation and just tried this code to demonstrate onChange usage (JSBIN).
var React= require('react');
var ControlledForm= React.createClass({
getInitialState: function() {
return {
value: "initial value"
};
},
handleChange: function(event) {
console.log(this.state.value);
this.setState({value: event.target.value});
console.log(this.state.value);
},
render: function() {
return (
<input type="text" value={this.state.value} onChange={this.handleChange}/>
);
}
});
React.render(
<ControlledForm/>,
document.getElementById('mount')
);
When I update the <input/> value in the browser, the second console.log inside the handleChange callback prints the same value as the first console.log, Why I can't see the result of this.setState({value: event.target.value}) in the scope of handleChange callback?
From React's documentation:
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value. There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
If you want a function to be executed after the state change occurs, pass it in as a callback.
this.setState({value: event.target.value}, function () {
console.log(this.state.value);
});
As mentioned in the React documentation, there is no guarantee of setState being fired synchronously, so your console.log may return the state prior to it updating.
Michael Parker mentions passing a callback within the setState. Another way to handle the logic after state change is via the componentDidUpdate lifecycle method, which is the method recommended in React docs.
Generally we recommend using componentDidUpdate() for such logic instead.
This is particularly useful when there may be successive setStates fired, and you would like to fire the same function after every state change. Rather than adding a callback to each setState, you could place the function inside of the componentDidUpdate, with specific logic inside if necessary.
// example
componentDidUpdate(prevProps, prevState) {
if (this.state.value > prevState.value) {
this.foo();
}
}
You could try using ES7 async/await. For instance using your example:
handleChange: async function(event) {
console.log(this.state.value);
await this.setState({value: event.target.value});
console.log(this.state.value);
}
Watch out the react lifecycle methods!
http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
https://reactjs.org/docs/react-component.html
I worked for several hours to find out that getDerivedStateFromProps will be called after every setState().
😂
Sometime this issue occurs with state.
In case of hooks, you should use useEffect hook, As below-
const [fruit, setFruit] = useState('');
setFruit('Apple');
useEffect(() => {
console.log('Fruit', fruit);
}, [fruit])
This saved my day, Hope will help you!!!
Accessing this.state after calling the setState method is not guaranteed to return the updated status due to the asynchronous nature of setState.
To guarantee an update after calling setState, there are two solutions you may pursue.
Solution 1: As mentioned in one of the above answers, put your code in the componentDidUpdate method
Solution 2: As mentioned in another of the above answers, pass your stuff as a callback
this.setState({value: myValue}, function () {
this.functionThatIsExecutedWhenStateIsUpdated();
});
It's important to note that these two solutions are not clearly interchangeable. The one cannot easily solve all the use-cases of the other. As a general rule, if you can, best practice says that solution 1 is preferred. But, there are use-cases where only solution 2 "more effectively" works such as the "update-my-view-and-post-my-data" use case. This use case goes like this:
After adding an item, say, "Add Schedule", I want to both add that item to a front-end list and immediately post the just-updated-list to the backend, as demonstrated in the concept below:
If you dont do either solution, i.e. if you only say this in your code:
addToItemArray = () => {
this.setState{{ scheduledItemsArray: newObjectListWithMax}}
this.postData();
}
<button className="btn btn-secondary btn-block" onClick={this.addToItemArray}>Add Shedule</button>
... you will post the list excluding the "Delivery to Max" item, because the state wont be updated when you this.postData() (again, because its asynchronous).
If you utilise solution 1, you would make a POST after typing in every character in the Schedule Name textbox!
There are other ways aswell to cater for this use-case but solution 2 best conveys the intent when reading the code.
Given the ubiquitous nature of this use case in virtually every web app, the callback technique explained by Michael's answer is an indispensable piece of code in every developers toolkit.
async-await syntax works perfectly for something like the following...
changeStateFunction = () => {
// Some Worker..
this.setState((prevState) => ({
year: funcHandleYear(),
month: funcHandleMonth()
}));
goNextMonth = async () => {
await this.changeStateFunction();
const history = createBrowserHistory();
history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}
goPrevMonth = async () => {
await this.changeStateFunction();
const history = createBrowserHistory();
history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}
React bathces different set state calls so that it can determine what is the most optimal strategy for rerendering the website is going to be.
Imagine you have an application where you have a lot of different components. Perhaps, with one button click you are updating the state in multiple components, not just on the current one. In this case, React does not want to just completely isolate and do all those different updates independently.
React wants to figure out if it can stack all these updates together, maybe there is a more optimal way of updating these components so that it is more performant. This is what React is doing behind the scenes. As a result, set state call is asynchronous call.
Simply putting - this.setState({data: value}) is asynchronous in
nature that means it moves out of the Call Stack and only comes back
to the Call Stack unless it is resolved.
Please read about Event Loop to have a clear picture about Asynchronous nature in JS and why it takes time to update -
https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4
Hence -
this.setState({data:value});
console.log(this.state.data); // will give undefined or unupdated value
as it takes time to update.
To achieve the above process -
this.setState({data:value},function () {
console.log(this.state.data);
});
I'm reading Forms section of reactjs documentation and just tried this code to demonstrate onChange usage (JSBIN).
var React= require('react');
var ControlledForm= React.createClass({
getInitialState: function() {
return {
value: "initial value"
};
},
handleChange: function(event) {
console.log(this.state.value);
this.setState({value: event.target.value});
console.log(this.state.value);
},
render: function() {
return (
<input type="text" value={this.state.value} onChange={this.handleChange}/>
);
}
});
React.render(
<ControlledForm/>,
document.getElementById('mount')
);
When I update the <input/> value in the browser, the second console.log inside the handleChange callback prints the same value as the first console.log, Why I can't see the result of this.setState({value: event.target.value}) in the scope of handleChange callback?
From React's documentation:
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value. There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
If you want a function to be executed after the state change occurs, pass it in as a callback.
this.setState({value: event.target.value}, function () {
console.log(this.state.value);
});
As mentioned in the React documentation, there is no guarantee of setState being fired synchronously, so your console.log may return the state prior to it updating.
Michael Parker mentions passing a callback within the setState. Another way to handle the logic after state change is via the componentDidUpdate lifecycle method, which is the method recommended in React docs.
Generally we recommend using componentDidUpdate() for such logic instead.
This is particularly useful when there may be successive setStates fired, and you would like to fire the same function after every state change. Rather than adding a callback to each setState, you could place the function inside of the componentDidUpdate, with specific logic inside if necessary.
// example
componentDidUpdate(prevProps, prevState) {
if (this.state.value > prevState.value) {
this.foo();
}
}
You could try using ES7 async/await. For instance using your example:
handleChange: async function(event) {
console.log(this.state.value);
await this.setState({value: event.target.value});
console.log(this.state.value);
}
Watch out the react lifecycle methods!
http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
https://reactjs.org/docs/react-component.html
I worked for several hours to find out that getDerivedStateFromProps will be called after every setState().
😂
Sometime this issue occurs with state.
In case of hooks, you should use useEffect hook, As below-
const [fruit, setFruit] = useState('');
setFruit('Apple');
useEffect(() => {
console.log('Fruit', fruit);
}, [fruit])
This saved my day, Hope will help you!!!
Accessing this.state after calling the setState method is not guaranteed to return the updated status due to the asynchronous nature of setState.
To guarantee an update after calling setState, there are two solutions you may pursue.
Solution 1: As mentioned in one of the above answers, put your code in the componentDidUpdate method
Solution 2: As mentioned in another of the above answers, pass your stuff as a callback
this.setState({value: myValue}, function () {
this.functionThatIsExecutedWhenStateIsUpdated();
});
It's important to note that these two solutions are not clearly interchangeable. The one cannot easily solve all the use-cases of the other. As a general rule, if you can, best practice says that solution 1 is preferred. But, there are use-cases where only solution 2 "more effectively" works such as the "update-my-view-and-post-my-data" use case. This use case goes like this:
After adding an item, say, "Add Schedule", I want to both add that item to a front-end list and immediately post the just-updated-list to the backend, as demonstrated in the concept below:
If you dont do either solution, i.e. if you only say this in your code:
addToItemArray = () => {
this.setState{{ scheduledItemsArray: newObjectListWithMax}}
this.postData();
}
<button className="btn btn-secondary btn-block" onClick={this.addToItemArray}>Add Shedule</button>
... you will post the list excluding the "Delivery to Max" item, because the state wont be updated when you this.postData() (again, because its asynchronous).
If you utilise solution 1, you would make a POST after typing in every character in the Schedule Name textbox!
There are other ways aswell to cater for this use-case but solution 2 best conveys the intent when reading the code.
Given the ubiquitous nature of this use case in virtually every web app, the callback technique explained by Michael's answer is an indispensable piece of code in every developers toolkit.
async-await syntax works perfectly for something like the following...
changeStateFunction = () => {
// Some Worker..
this.setState((prevState) => ({
year: funcHandleYear(),
month: funcHandleMonth()
}));
goNextMonth = async () => {
await this.changeStateFunction();
const history = createBrowserHistory();
history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}
goPrevMonth = async () => {
await this.changeStateFunction();
const history = createBrowserHistory();
history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}
React bathces different set state calls so that it can determine what is the most optimal strategy for rerendering the website is going to be.
Imagine you have an application where you have a lot of different components. Perhaps, with one button click you are updating the state in multiple components, not just on the current one. In this case, React does not want to just completely isolate and do all those different updates independently.
React wants to figure out if it can stack all these updates together, maybe there is a more optimal way of updating these components so that it is more performant. This is what React is doing behind the scenes. As a result, set state call is asynchronous call.
Simply putting - this.setState({data: value}) is asynchronous in
nature that means it moves out of the Call Stack and only comes back
to the Call Stack unless it is resolved.
Please read about Event Loop to have a clear picture about Asynchronous nature in JS and why it takes time to update -
https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4
Hence -
this.setState({data:value});
console.log(this.state.data); // will give undefined or unupdated value
as it takes time to update.
To achieve the above process -
this.setState({data:value},function () {
console.log(this.state.data);
});
I'm reading Forms section of reactjs documentation and just tried this code to demonstrate onChange usage (JSBIN).
var React= require('react');
var ControlledForm= React.createClass({
getInitialState: function() {
return {
value: "initial value"
};
},
handleChange: function(event) {
console.log(this.state.value);
this.setState({value: event.target.value});
console.log(this.state.value);
},
render: function() {
return (
<input type="text" value={this.state.value} onChange={this.handleChange}/>
);
}
});
React.render(
<ControlledForm/>,
document.getElementById('mount')
);
When I update the <input/> value in the browser, the second console.log inside the handleChange callback prints the same value as the first console.log, Why I can't see the result of this.setState({value: event.target.value}) in the scope of handleChange callback?
From React's documentation:
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value. There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
If you want a function to be executed after the state change occurs, pass it in as a callback.
this.setState({value: event.target.value}, function () {
console.log(this.state.value);
});
As mentioned in the React documentation, there is no guarantee of setState being fired synchronously, so your console.log may return the state prior to it updating.
Michael Parker mentions passing a callback within the setState. Another way to handle the logic after state change is via the componentDidUpdate lifecycle method, which is the method recommended in React docs.
Generally we recommend using componentDidUpdate() for such logic instead.
This is particularly useful when there may be successive setStates fired, and you would like to fire the same function after every state change. Rather than adding a callback to each setState, you could place the function inside of the componentDidUpdate, with specific logic inside if necessary.
// example
componentDidUpdate(prevProps, prevState) {
if (this.state.value > prevState.value) {
this.foo();
}
}
You could try using ES7 async/await. For instance using your example:
handleChange: async function(event) {
console.log(this.state.value);
await this.setState({value: event.target.value});
console.log(this.state.value);
}
Watch out the react lifecycle methods!
http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
https://reactjs.org/docs/react-component.html
I worked for several hours to find out that getDerivedStateFromProps will be called after every setState().
😂
Sometime this issue occurs with state.
In case of hooks, you should use useEffect hook, As below-
const [fruit, setFruit] = useState('');
setFruit('Apple');
useEffect(() => {
console.log('Fruit', fruit);
}, [fruit])
This saved my day, Hope will help you!!!
Accessing this.state after calling the setState method is not guaranteed to return the updated status due to the asynchronous nature of setState.
To guarantee an update after calling setState, there are two solutions you may pursue.
Solution 1: As mentioned in one of the above answers, put your code in the componentDidUpdate method
Solution 2: As mentioned in another of the above answers, pass your stuff as a callback
this.setState({value: myValue}, function () {
this.functionThatIsExecutedWhenStateIsUpdated();
});
It's important to note that these two solutions are not clearly interchangeable. The one cannot easily solve all the use-cases of the other. As a general rule, if you can, best practice says that solution 1 is preferred. But, there are use-cases where only solution 2 "more effectively" works such as the "update-my-view-and-post-my-data" use case. This use case goes like this:
After adding an item, say, "Add Schedule", I want to both add that item to a front-end list and immediately post the just-updated-list to the backend, as demonstrated in the concept below:
If you dont do either solution, i.e. if you only say this in your code:
addToItemArray = () => {
this.setState{{ scheduledItemsArray: newObjectListWithMax}}
this.postData();
}
<button className="btn btn-secondary btn-block" onClick={this.addToItemArray}>Add Shedule</button>
... you will post the list excluding the "Delivery to Max" item, because the state wont be updated when you this.postData() (again, because its asynchronous).
If you utilise solution 1, you would make a POST after typing in every character in the Schedule Name textbox!
There are other ways aswell to cater for this use-case but solution 2 best conveys the intent when reading the code.
Given the ubiquitous nature of this use case in virtually every web app, the callback technique explained by Michael's answer is an indispensable piece of code in every developers toolkit.
async-await syntax works perfectly for something like the following...
changeStateFunction = () => {
// Some Worker..
this.setState((prevState) => ({
year: funcHandleYear(),
month: funcHandleMonth()
}));
goNextMonth = async () => {
await this.changeStateFunction();
const history = createBrowserHistory();
history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}
goPrevMonth = async () => {
await this.changeStateFunction();
const history = createBrowserHistory();
history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}
React bathces different set state calls so that it can determine what is the most optimal strategy for rerendering the website is going to be.
Imagine you have an application where you have a lot of different components. Perhaps, with one button click you are updating the state in multiple components, not just on the current one. In this case, React does not want to just completely isolate and do all those different updates independently.
React wants to figure out if it can stack all these updates together, maybe there is a more optimal way of updating these components so that it is more performant. This is what React is doing behind the scenes. As a result, set state call is asynchronous call.
Simply putting - this.setState({data: value}) is asynchronous in
nature that means it moves out of the Call Stack and only comes back
to the Call Stack unless it is resolved.
Please read about Event Loop to have a clear picture about Asynchronous nature in JS and why it takes time to update -
https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4
Hence -
this.setState({data:value});
console.log(this.state.data); // will give undefined or unupdated value
as it takes time to update.
To achieve the above process -
this.setState({data:value},function () {
console.log(this.state.data);
});
When a react component state changes, the render method is called. Hence for any state change, an action can be performed in the render methods body. Is there a particular use case for the setState callback then?
Yes there is, since setState works in an asynchronous way. That means after calling setState the this.state variable is not immediately changed. so if you want to perform an action immediately after setting state on a state variable and then return a result, a callback will be useful
Consider the example below
....
changeTitle: function changeTitle (event) {
this.setState({ title: event.target.value });
this.validateTitle();
},
validateTitle: function validateTitle () {
if (this.state.title.length === 0) {
this.setState({ titleError: "Title can't be blank" });
}
},
....
The above code may not work as expected since the title variable may not have mutated before validation is performed on it. Now you may wonder that we can perform the validation in the render() function itself but it would be better and a cleaner way if we can handle this in the changeTitle function itself since that would make your code more organised and understandable
In this case callback is useful
....
changeTitle: function changeTitle (event) {
this.setState({ title: event.target.value }, function() {
this.validateTitle();
});
},
validateTitle: function validateTitle () {
if (this.state.title.length === 0) {
this.setState({ titleError: "Title can't be blank" });
}
},
....
Another example will be when you want to dispatch and action when the state changed. you will want to do it in a callback and not the render() as it will be called everytime rerendering occurs and hence many such scenarios are possible where you will need callback.
Another case is a API Call
A case may arise when you need to make an API call based on a particular state change, if you do that in the render method, it will be called on every render onState change or because some Prop passed down to the Child Component changed.
In this case you would want to use a setState callback to pass the updated state value to the API call
....
changeTitle: function (event) {
this.setState({ title: event.target.value }, () => this.APICallFunction());
},
APICallFunction: function () {
// Call API with the updated value
}
....
this.setState({
name:'value'
},() => {
console.log(this.state.name);
});
The 1. usecase which comes into my mind, is an api call, which should't go into the render, because it will run for each state change. And the API call should be only performed on special state change, and not on every render.
changeSearchParams = (params) => {
this.setState({ params }, this.performSearch)
}
performSearch = () => {
API.search(this.state.params, (result) => {
this.setState({ result })
});
}
Hence for any state change, an action can be performed in the render methods body.
Very bad practice, because the render-method should be pure, it means no actions, state changes, api calls, should be performed, just composite your view and return it. Actions should be performed on some events only. Render is not an event, but componentDidMount for example.
Consider setState call
this.setState({ counter: this.state.counter + 1 })
IDEA
setState may be called in async function
So you cannot rely on this. If the above call was made inside a async function this will refer to state of component at that point of time but we expected this to refer to property inside state at time setState calling or beginning of async task. And as task was async call thus that property may have changed in time being. Thus it is unreliable to use this keyword to refer to some property of state thus we use callback function whose arguments are previousState and props which means when async task was done and it was time to update state using setState call prevState will refer to state now when setState has not started yet. Ensuring reliability that nextState would not be corrupted.
Wrong Code: would lead to corruption of data
this.setState(
{counter:this.state.counter+1}
);
Correct Code with setState having call back function:
this.setState(
(prevState,props)=>{
return {counter:prevState.counter+1};
}
);
Thus whenever we need to update our current state to next state based on value possed by property just now and all this is happening in async fashion it is good idea to use setState as callback function.
I have tried to explain it in codepen here CODE PEN
Sometimes we need a code block where we need to perform some operation right after setState where we are sure the state is being updated. That is where setState callback comes into play
For example, there was a scenario where I needed to enable a modal for 2 customers out of 20 customers, for the customers where we enabled it, there was a set of time taking API calls, so it looked like this
async componentDidMount() {
const appConfig = getCustomerConfig();
this.setState({enableModal: appConfig?.enableFeatures?.paymentModal }, async
()=>{
if(this.state.enableModal){
//make some API call for data needed in poput
}
});
}
enableModal boolean was required in UI blocks in the render function as well, that's why I did setState here, otherwise, could've just checked condition once and either called API set or not.