When to use React setState callback

ReactjsCallbackSetstate

Reactjs Problem Overview


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?

Reactjs Solutions


Solution 1 - Reactjs

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
}
....

Solution 2 - Reactjs

this.setState({
    name:'value' 
},() => {
    console.log(this.state.name);
});

Solution 3 - Reactjs

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.

Solution 4 - Reactjs

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

Solution 5 - Reactjs

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSahil JainView Question on Stackoverflow
Solution 1 - ReactjsShubham KhatriView Answer on Stackoverflow
Solution 2 - ReactjsAraz BabayevView Answer on Stackoverflow
Solution 3 - ReactjswebdebView Answer on Stackoverflow
Solution 4 - ReactjsAniket JhaView Answer on Stackoverflow
Solution 5 - ReactjsAbdul AzeemView Answer on Stackoverflow