when to use react setstate callback

  • Last Update :
  • Techknowledgy :

In this case you would want to use a setState callback to pass the updated state value to the API call 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 1 When you want call a function after the state changed you can use the method. – Araz Babayev May 30, 2018 at 12:53

This is our Splunktool team suggestion ✌, we tried and its working fine
//You need to use useEffect hook to achieve this.const [counter, setCounter] = useState(0);​
const doSomething = () => {
   setCounter(123);
}​
useEffect(() => {
   console.log('Do something after counter has changed', counter);
}, [counter]);​

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"
         });
      }
   },
   ....
5._
this.setState({
   name: 'value'
}, () => {
   console.log(this.state.name);
});

Suggestion : 2

Let’s see how to perform a callback inside a React class component after setState executes: To solve this specific React issue, we can use the setState function’s callback. Whatever we pass into setState’s second argument executes after the setState function updates. Here’s something extremely important to know about state in React: updating a React component’s state is asynchronous. It does not happen immediately.

1._
import React, { Component } from 'react';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      age: 0,
    };
  }
  
  // this.checkAge is passed as the callback to setState
  updateAge = (value) => {
    this.setState({ age: value}, this.checkAge);
  };

  checkAge = () => {
    const { age } = this.state;
    if (age !== 0 && age >= 21) {
      // Make API call to /beer
    } else {
      // Throw error 404, beer not found
    }
  };

  render() {
    const { age } = this.state;
    return (
      <div>
        <p>Drinking Age Checker</p>
        <input
          type="number"
          value={age}
          onChange={e => this.updateAge(e.target.value)}
        />
      </div>
    );
  }

}

export default App;
2._
import React, { useEffect, useState } from 'react';

function App() {
  const [age, setAge] = useState(0);
  
  updateAge(value) {
    setAge(value);
  };

  useEffect(() => {
    if (age !== 0 && age >= 21) {
      // Make API call to /beer
    } else {
      // Throw error 404, beer not found
    }
  }, [age]);

  return (
    <div>
      <p>Drinking Age Checker</p>
      <input
        type="number"
        value={age} 
        onChange={e => setAge(e.target.value)}
      />
    </div>
  );
}

export default App;
3._
useEffect(() => {
   if (age !== 0 && age >= 21) {
      // Make API call to /beer
   } else {
      // Throw error 404, beer not found
   }
}, [age]);

Suggestion : 3

Inside your project, create a new file ChallanGenerator.js or whichever name you prefer and start writing this code. Create a new project by the following command: Again start with creating a new file ChallanGenerator.js and this time it’ll be a functional component. So, basically to perform an action such as making an AJAX request or throwing an error, after calling setState in a react component we use setState Callback function.

 npx create - react - app challan - generator

Now, go to the project directory and start it-

cd challan - generator

npm start

Suggestion : 4

SetState is an asynchronous method. Asynchronous means that the remaining code will get executed while the current action is being performed. Whereas synchronous code will block the code execution while the current action is being performed. So, if you want an action to be performed only after the state has been updated you can make use of a call back function. This callback function is put as an argument to setstate method. This is the exact purpose of defining a callback function as an argument of setState. Approach: Let us create a React project and then we will create a UI to showcase the above purpose. The user will update the state and then only action will be performed through the callback function. Step 1: Create a react application by typing the following command in the terminal.

Creating React Project:

npx create - react - app project_name

Step 2: Now, go to the project folder i.e. project_name by running the following command.

cd project_name

Project Structure: It will look like the following:

Example: Let us create a button that will act as a counter. Users can click on the button to increase the value. Only after the state updates i.e. the value increases the user can see the updated value in UI.

import React from "react";
  
class App extends React.Component {
    constructor() {
        super();
        this.state = {
            value: 0
        }
    }
  
    call() {
        this.setState({ value: this.state.value + 1 }, () =>
            console.log("Updated Value :" + this.state.value)
        );
    }
  
    render() {
        return (
            <div>
                <button onClick={() => { this.call() }}>
                    Click to update state!
                </button>
            </div>
        );
    }
}
  
export default App;

Suggestion : 5

The second parameter to setState() is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead. You should not call setState() in componentWillUnmount() because the component will never be re-rendered. Once a component instance is unmounted, it will never be mounted again. An update can be caused by changes to props or state. These methods are called in the following order when a component is being re-rendered:

1._
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
2._
render()
3._
constructor(props)
5._
constructor(props) {
   super(props);
   // Don't do this!
   this.state = {
      color: props.color
   };
}
6._
componentDidMount()

Suggestion : 6

When the state is actually set can vary. Usually it happens on the next render, but it can sometimes be batched for performance. The setState function takes an optional callback parameter that can be used to make updates after the state is changed. This function will get called once the state has been updated, and the callback will receive the updated value of the state. To update the state of a component, you use the setState method. However it is easy to forget that the setState method is asynchronous, causing tricky to debug issues in your code. The setState function also does not return a Promise. Using async/await or anything similar will not work.

1._
handleButtonClicked = evt => {
   this.setState({
      name: evt.currentTarget.value
   })
   this.props.callback(this.state.name) // Will send the old value for name
}
2._
handleButtonClicked = evt => {
   this.setState({
      name: evt.currentTarget.value
   }, () => {
      this.props.callback(this.state.name)
   })
}

Suggestion : 7

In React functional components, a callback function for anything can be implemented using the useEffect hook. We will be using the same to provide callback functionality to our useState hook to make it function similar to setState. Note: If you want the changes performed in the callback function to be reflected in the component’s rendered output, you would want to use useLayoutEffect instead of the useEffect react hook. The callback function would not be invoked in this case, and it would throw a warning instead. It recommends using useEffect instead. Before jumping into the implementation using useEffect, let us first examine why we need the callback in the first place.

Originally published at https://www.wisdomgeek.com on December 15, 2020.

If you have been writing class components for a while, you might be familiar with the callback functionality that the setState function provides. setState allows a second parameter to be passed to it as a callback. The callback function is invoked whenever the state of the function gets updated.

this.setState(newState, callbackFunction)

But, this callback mechanism does not exist with functional components.

const [state, setState] = useState();
setState(newState, callbackFunction)

The state update usually happens on the next render, but even that can vary. Batching updates is up to react, and there is nothing we can do to change that.

Considering the following example:

export default function useProgressPercentage() {
   const [percentage, setPercentage] = useState(0);
   const [completedRequests, setCompletedRequests] = useState(0);
   const [totalRequests, setTotalRequests] = useState(1);

   const incrementCompletedRequestCount = () => {
      setCompletedRequests((completedRequests) => completedRequests + 1);
      setPercentage(
         Number(((completedRequests / totalRequests) * 100).toFixed(2))
      );
   };
   const incrementDataLoadRequestCount = () => {
      setTotalRequests((totalRequests) => totalRequests + 1);
      setPercentage(
         Number(((completedRequests / totalRequests) * 100).toFixed(2))
      );
   };

   return [
      percentage,
      incrementCompletedRequestCount,
      incrementDataLoadRequestCount,
   ];
}

We will be making use of the dependency array of the useEffect to achieve this. If you are not familiar with it, we recommend reading our post on useEffect react hook.

const [state, setState] = useState();
useEffect(() => {
   callbackFunction()
}, [state])

Therefore, our percentage hook can be updated as follows:

export default function useProgressPercentage() {
   const [percentage, setPercentage] = useState(0);
   const [completedRequests, setCompletedRequests] = useState(0);
   const [totalRequests, setTotalRequests] = useState(1);

   useEffect(() => {
      setPercentage(
         Number(((completedRequests / totalRequests) * 100).toFixed(2))
      );
   }, [completedRequests, totalRequests]);

   const incrementCompletedRequestCount = () => {
      setCompletedRequests((completedRequests) => completedRequests + 1);
   };
   const incrementDataLoadRequestCount = () => {
      setTotalRequests((totalRequests) => totalRequests + 1);
   };

   return [
      percentage,
      incrementCompletedRequestCount,
      incrementDataLoadRequestCount,
   ];
}