To perform an action in a React component after calling setState, such as making an AJAX request or throwing an error, we use the setState callback.
Here’s something extremely important to know about state in React: updating a React component’s state is asynchronous. It does not happen immediately.
Therefore you will run into scenarios whereby parts of your code run before state has had a chance to update.
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.
setState Callback in a Class Component
Let’s see how to perform a callback inside a React class component after setState executes:
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;
This nifty drinking age checker component displays a single input. After changing the value inside that input, it changes the age value inside of its state.
Focus in on the checkAge function. That’s where the setState function gets called. Look at the second argument inside that setState function: it’s calling checkAge.
That’s the callback function that will be executed after the age state value is updated.
What we’re essentially doing is waiting until age has fully updated in state to then make the call to check age. If we didn’t wait, we might be checking an older age value.
Cheers!
setState Callback in a Functional Component
React 16.8 introduced Hooks which gave us a way to add state to functional components through the useState Hook.
However, the useState Hook does not have a second callback argument.
Learn more about the useEffect Hook with my tutorial Simplifying React State and the useState Hook.
Instead, we use the useEffect Hook and its second argument, which is an array of dependencies.
Let’s take a look at the same example above, but this time in the context of a functional component that uses the useState and useEffect Hooks:
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;
If you haven’t seen Hooks before, why not check out my Simple Introduction to React Hooks.
Much of this component is the same as the Class component, with one vital difference: the useEffect function. Let’s break it down line-by-line:
useEffect(() => {
if (age !== 0 && age >= 21) {
// Make API call to /beer
} else {
// Throw error 404, beer not found
}
}, [age]);
Starting from the bottom, we see parentheses with the age state variable inside of them. This is what’s called the dependency array, and it tells this particular useEffect function to listen out for any changes to the age state variable.
Once the age state variable changes, this useEffect function executes. It’s the equivalent of the setState callback function inside of React class components in our first example. To learn more about the differences between class based and functional components in React check out this guide
You can have multiple useEffect functions in a single component.
💬 Leave a comment