This is one stop global knowledge base where you can learn about all the products, solutions and support features.
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
The introduction page used this example to get familiar with Hooks:
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
We’ll start learning about Hooks by comparing this code to an equivalent class example.
If you used classes in React before, this code should look familiar:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
The state starts as
{ count: 0 }
, and we increment
state.count
when the user clicks a button by calling
this.setState()
. We’ll use snippets from this class throughout the page.
Note
You might be wondering why we’re using a counter here instead of a more realistic example. This is to help us focus on the API while we’re still making our first steps with Hooks.
As a reminder, function components in React look like this:
const Example = (props) => {
// You can use Hooks here!
return <div />;
}
or this:
function Example(props) {
// You can use Hooks here!
return <div />;
}
You might have previously known these as “stateless components”. We’re now introducing the ability to use React state from these, so we prefer the name “function components”.
Hooks don’t work inside classes. But you can use them instead of writing classes.
Our new example starts by importing the
useState
Hook from React:
import React, { useState } from 'react';
function Example() {
// ...
}
What is a Hook?
A Hook is a special function that lets you “hook into” React features. For example,
useState
is a Hook that lets you add React state to function components. We’ll learn other Hooks later.
When would I use a Hook? If you write a function component and realize you need to add some state to it, previously you had to convert it to a class. Now you can use a Hook inside the existing function component. We’re going to do that right now!
Note:
There are some special rules about where you can and can’t use Hooks within a component. We’ll learn them in Rules of Hooks.
In a class, we initialize the
count
state to
0
by setting
this.state
to
{ count: 0 }
in the constructor:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 }; }
In a function component, we have no
this
, so we can’t assign or read
this.state
. Instead, we call the
useState
Hook directly inside our component:
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0);
What does calling
useState
do?
It declares a “state variable”. Our variable is called
count
but we could call it anything else, like
banana
. This is a way to “preserve” some values between the function calls —
useState
is a new way to use the exact same capabilities that
this.state
provides in a class. Normally, variables “disappear” when the function exits but state variables are preserved by React.
What do we pass to
useState
as an argument?
The only argument to the
useState()
Hook is the initial state. Unlike with classes, the state doesn’t have to be an object. We can keep a number or a string if that’s all we need. In our example, we just want a number for how many times the user clicked, so pass
0
as initial state for our variable. (If we wanted to store two different values in state, we would call
useState()
twice.)
What does
useState
return?
It returns a pair of values: the current state and a function that updates it. This is why we write
const [count, setCount] = useState()
. This is similar to
this.state.count
and
this.setState
in a class, except you get them in a pair. If you’re not familiar with the syntax we used, we’ll come back to it at the bottom of this page.
Now that we know what the
useState
Hook does, our example should make more sense:
import React, { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0);
We declare a state variable called
count
, and set it to
0
. React will remember its current value between re-renders, and provide the most recent one to our function. If we want to update the current
count
, we can call
setCount
.
Note
You might be wondering: why is
useState
not namedcreateState
instead?
“Create” wouldn’t be quite accurate because the state is only created the first time our component renders. During the next renders,
useState
gives us the current state. Otherwise it wouldn’t be “state” at all! There’s also a reason why Hook names always start withuse
. We’ll learn why later in the Rules of Hooks.
When we want to display the current count in a class, we read
this.state.count
:
<p>You clicked {this.state.count} times</p>
In a function, we can use
count
directly:
<p>You clicked {count} times</p>
In a class, we need to call
this.setState()
to update the
count
state:
<button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me
</button>
In a function, we already have
setCount
and
count
as variables so we don’t need
this
:
<button onClick={() => setCount(count + 1)}> Click me
</button>
Let’s now recap what we learned line by line and check our understanding.
1: import React, { useState } from 'react'; 2:
3: function Example() {
4: const [count, setCount] = useState(0); 5:
6: return (
7: <div>
8: <p>You clicked {count} times</p>
9: <button onClick={() => setCount(count + 1)}>10: Click me
11: </button>
12: </div>
13: );
14: }
useState
Hook from React. It lets us keep local state in a function component.
Example
component, we declare a new state variable by calling the
useState
Hook. It returns a pair of values, to which we give names. We’re calling our variable
count
because it holds the number of button clicks. We initialize it to zero by passing
0
as the only
useState
argument. The second returned item is itself a function. It lets us update the
count
so we’ll name it
setCount
.
setCount
with a new value. React will then re-render the
Example
component, passing the new
count
value to it.
This might seem like a lot to take in at first. Don’t rush it! If you’re lost in the explanation, look at the code above again and try to read it from top to bottom. We promise that once you try to “forget” how state works in classes, and look at this code with fresh eyes, it will make sense.
You might have noticed the square brackets when we declare a state variable:
const [count, setCount] = useState(0);
The names on the left aren’t a part of the React API. You can name your own state variables:
const [fruit, setFruit] = useState('banana');
This JavaScript syntax is called “array destructuring”. It means that we’re making two new variables
fruit
and
setFruit
, where
fruit
is set to the first value returned by
useState
, and
setFruit
is the second. It is equivalent to this code:
var fruitStateVariable = useState('banana'); // Returns a pair
var fruit = fruitStateVariable[0]; // First item in a pair
var setFruit = fruitStateVariable[1]; // Second item in a pair
When we declare a state variable with
useState
, it returns a pair — an array with two items. The first item is the current value, and the second is a function that lets us update it. Using
[0]
and
[1]
to access them is a bit confusing because they have a specific meaning. This is why we use array destructuring instead.
Note
You might be curious how React knows which component
useState
corresponds to since we’re not passing anything likethis
back to React. We’ll answer this question and many others in the FAQ section.
Declaring state variables as a pair of
[something, setSomething]
is also handy because it lets us give
different
names to different state variables if we want to use more than one:
function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
In the above component, we have
age
,
fruit
, and
todos
as local variables, and we can update them individually:
function handleOrangeClick() {
// Similar to this.setState({ fruit: 'orange' })
setFruit('orange');
}
You
don’t have to
use many state variables. State variables can hold objects and arrays just fine, so you can still group related data together. However, unlike
this.setState
in a class, updating a state variable always
replaces
it instead of merging it.
We provide more recommendations on splitting independent state variables in the FAQ.
On this page we’ve learned about one of the Hooks provided by React, called
useState
. We’re also sometimes going to refer to it as the “State Hook”. It lets us add local state to React function components — which we did for the first time ever!
We also learned a little bit more about what Hooks are. Hooks are functions that let you “hook into” React features from function components. Their names always start with
use
, and there are more Hooks we haven’t seen yet.
Now let’s continue by learning the next Hook:
useEffect
.
It lets you perform side effects in components, and is similar to lifecycle methods in classes.
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
The Effect Hook lets you perform side effects in function components:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate: useEffect(() => { // Update the document title using the browser API document.title = `You clicked ${count} times`; });
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
This snippet is based on the counter example from the previous page, but we added a new feature to it: we set the document title to a custom message including the number of clicks.
Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects. Whether or not you’re used to calling these operations “side effects” (or just “effects”), you’ve likely performed them in your components before.
Tip
If you’re familiar with React class lifecycle methods, you can think of
useEffect
Hook ascomponentDidMount
,componentDidUpdate
, andcomponentWillUnmount
combined.
There are two common kinds of side effects in React components: those that don’t require cleanup, and those that do. Let’s look at this distinction in more detail.
Sometimes, we want to run some additional code after React has updated the DOM. Network requests, manual DOM mutations, and logging are common examples of effects that don’t require a cleanup. We say that because we can run them and immediately forget about them. Let’s compare how classes and Hooks let us express such side effects.
In React class components, the
render
method itself shouldn’t cause side effects. It would be too early — we typically want to perform our effects
after
React has updated the DOM.
This is why in React classes, we put side effects into
componentDidMount
and
componentDidUpdate
. Coming back to our example, here is a React counter class component that updates the document title right after React makes changes to the DOM:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
componentDidMount() { document.title = `You clicked ${this.state.count} times`; } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; }
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
Note how we have to duplicate the code between these two lifecycle methods in class.
This is because in many cases we want to perform the same side effect regardless of whether the component just mounted, or if it has been updated. Conceptually, we want it to happen after every render — but React class components don’t have a method like this. We could extract a separate method but we would still have to call it in two places.
Now let’s see how we can do the same with the
useEffect
Hook.
We’ve already seen this example at the top of this page, but let’s take a closer look at it:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => { document.title = `You clicked ${count} times`; });
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
What does
useEffect
do?
By using this Hook, you tell React that your component needs to do something after render. React will remember the function you passed (we’ll refer to it as our “effect”), and call it later after performing the DOM updates. In this effect, we set the document title, but we could also perform data fetching or call some other imperative API.
Why is
useEffect
called inside a component?
Placing
useEffect
inside the component lets us access the
count
state variable (or any props) right from the effect. We don’t need a special API to read it — it’s already in the function scope. Hooks embrace JavaScript closures and avoid introducing React-specific APIs where JavaScript already provides a solution.
Does
useEffect
run after every render?
Yes! By default, it runs both after the first render
and
after every update. (We will later talk about how to customize this.) Instead of thinking in terms of “mounting” and “updating”, you might find it easier to think that effects happen “after render”. React guarantees the DOM has been updated by the time it runs the effects.
Now that we know more about effects, these lines should make sense:
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
}
We declare the
count
state variable, and then we tell React we need to use an effect. We pass a function to the
useEffect
Hook. This function we pass
is
our effect. Inside our effect, we set the document title using the
document.title
browser API. We can read the latest
count
inside the effect because it’s in the scope of our function. When React renders our component, it will remember the effect we used, and then run our effect after updating the DOM. This happens for every render, including the first one.
Experienced JavaScript developers might notice that the function passed to
useEffect
is going to be different on every render. This is intentional. In fact, this is what lets us read the
count
value from inside the effect without worrying about it getting stale. Every time we re-render, we schedule a
different
effect, replacing the previous one. In a way, this makes the effects behave more like a part of the render result — each effect “belongs” to a particular render. We will see more clearly why this is useful later on this page.
Tip
Unlike
componentDidMount
orcomponentDidUpdate
, effects scheduled withuseEffect
don’t block the browser from updating the screen. This makes your app feel more responsive. The majority of effects don’t need to happen synchronously. In the uncommon cases where they do (such as measuring the layout), there is a separateuseLayoutEffect
Hook with an API identical touseEffect
.
Earlier, we looked at how to express side effects that don’t require any cleanup. However, some effects do. For example, we might want to set up a subscription to some external data source. In that case, it is important to clean up so that we don’t introduce a memory leak! Let’s compare how we can do it with classes and with Hooks.
In a React class, you would typically set up a subscription in
componentDidMount
, and clean it up in
componentWillUnmount
. For example, let’s say we have a
ChatAPI
module that lets us subscribe to a friend’s online status. Here’s how we might subscribe and display that status using a class:
class FriendStatus extends React.Component {
constructor(props) {
super(props);
this.state = { isOnline: null };
this.handleStatusChange = this.handleStatusChange.bind(this);
}
componentDidMount() { ChatAPI.subscribeToFriendStatus( this.props.friend.id, this.handleStatusChange ); } componentWillUnmount() { ChatAPI.unsubscribeFromFriendStatus( this.props.friend.id, this.handleStatusChange ); } handleStatusChange(status) { this.setState({ isOnline: status.isOnline }); }
render() {
if (this.state.isOnline === null) {
return 'Loading...';
}
return this.state.isOnline ? 'Online' : 'Offline';
}
}
Notice how
componentDidMount
and
componentWillUnmount
need to mirror each other. Lifecycle methods force us to split this logic even though conceptually code in both of them is related to the same effect.
Note
Eagle-eyed readers may notice that this example also needs a
componentDidUpdate
method to be fully correct. We’ll ignore this for now but will come back to it in a later section of this page.
Let’s see how we could write this component with Hooks.
You might be thinking that we’d need a separate effect to perform the cleanup. But code for adding and removing a subscription is so tightly related that
useEffect
is designed to keep it together. If your effect returns a function, React will run it when it is time to clean up:
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); // Specify how to clean up after this effect: return function cleanup() { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; });
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
Why did we return a function from our effect? This is the optional cleanup mechanism for effects. Every effect may return a function that cleans up after it. This lets us keep the logic for adding and removing subscriptions close to each other. They’re part of the same effect!
When exactly does React clean up an effect? React performs the cleanup when the component unmounts. However, as we learned earlier, effects run for every render and not just once. This is why React also cleans up effects from the previous render before running the effects next time. We’ll discuss why this helps avoid bugs and how to opt out of this behavior in case it creates performance issues later below.
Note
We don’t have to return a named function from the effect. We called it
cleanup
here to clarify its purpose, but you could return an arrow function or call it something different.
We’ve learned that
useEffect
lets us express different kinds of side effects after a component renders. Some effects might require cleanup so they return a function:
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
Other effects might not have a cleanup phase, and don’t return anything.
useEffect(() => {
document.title = `You clicked ${count} times`;
});
The Effect Hook unifies both use cases with a single API.
If you feel like you have a decent grasp on how the Effect Hook works, or if you feel overwhelmed, you can jump to the next page about Rules of Hooks now.
We’ll continue this page with an in-depth look at some aspects of
useEffect
that experienced React users will likely be curious about. Don’t feel obligated to dig into them now. You can always come back to this page to learn more details about the Effect Hook.
One of the problems we outlined in the Motivation for Hooks is that class lifecycle methods often contain unrelated logic, but related logic gets broken up into several methods. Here is a component that combines the counter and the friend status indicator logic from the previous examples:
class FriendStatusWithCounter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0, isOnline: null };
this.handleStatusChange = this.handleStatusChange.bind(this);
}
componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
handleStatusChange(status) {
this.setState({
isOnline: status.isOnline
});
}
// ...
Note how the logic that sets
document.title
is split between
componentDidMount
and
componentDidUpdate
. The subscription logic is also spread between
componentDidMount
and
componentWillUnmount
. And
componentDidMount
contains code for both tasks.
So, how can Hooks solve this problem? Just like you can use the State Hook more than once, you can also use several effects. This lets us separate unrelated logic into different effects:
function FriendStatusWithCounter(props) {
const [count, setCount] = useState(0);
useEffect(() => { document.title = `You clicked ${count} times`;
});
const [isOnline, setIsOnline] = useState(null);
useEffect(() => { function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
// ...
}
Hooks let us split the code based on what it is doing rather than a lifecycle method name. React will apply every effect used by the component, in the order they were specified.
If you’re used to classes, you might be wondering why the effect cleanup phase happens after every re-render, and not just once during unmounting. Let’s look at a practical example to see why this design helps us create components with fewer bugs.
Earlier on this page, we introduced an example
FriendStatus
component that displays whether a friend is online or not. Our class reads
friend.id
from
this.props
, subscribes to the friend status after the component mounts, and unsubscribes during unmounting:
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
But what happens if the
friend
prop changes
while the component is on the screen? Our component would continue displaying the online status of a different friend. This is a bug. We would also cause a memory leak or crash when unmounting since the unsubscribe call would use the wrong friend ID.
In a class component, we would need to add
componentDidUpdate
to handle this case:
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentDidUpdate(prevProps) { // Unsubscribe from the previous friend.id ChatAPI.unsubscribeFromFriendStatus( prevProps.friend.id, this.handleStatusChange ); // Subscribe to the next friend.id ChatAPI.subscribeToFriendStatus( this.props.friend.id, this.handleStatusChange ); }
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
Forgetting to handle
componentDidUpdate
properly is a common source of bugs in React applications.
Now consider the version of this component that uses Hooks:
function FriendStatus(props) {
// ...
useEffect(() => {
// ...
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
It doesn’t suffer from this bug. (But we also didn’t make any changes to it.)
There is no special code for handling updates because
useEffect
handles them
by default
. It cleans up the previous effects before applying the next effects. To illustrate this, here is a sequence of subscribe and unsubscribe calls that this component could produce over time:
// Mount with { friend: { id: 100 } } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange); // Run first effect
// Update with { friend: { id: 200 } } props
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(200, handleStatusChange); // Run next effect
// Update with { friend: { id: 300 } } props
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(300, handleStatusChange); // Run next effect
// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // Clean up last effect
This behavior ensures consistency by default and prevents bugs that are common in class components due to missing update logic.
In some cases, cleaning up or applying the effect after every render might create a performance problem. In class components, we can solve this by writing an extra comparison with
prevProps
or
prevState
inside
componentDidUpdate
:
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} times`;
}
}
This requirement is common enough that it is built into the
useEffect
Hook API. You can tell React to
skip
applying an effect if certain values haven’t changed between re-renders. To do so, pass an array as an optional second argument to
useEffect
:
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
In the example above, we pass
[count]
as the second argument. What does this mean? If the
count
is
5
, and then our component re-renders with
count
still equal to
5
, React will compare
[5]
from the previous render and
[5]
from the next render. Because all items in the array are the same (
5 === 5
), React would skip the effect. That’s our optimization.
When we render with
count
updated to
6
, React will compare the items in the
[5]
array from the previous render to items in the
[6]
array from the next render. This time, React will re-apply the effect because
5 !== 6
. If there are multiple items in the array, React will re-run the effect even if just one of them is different.
This also works for effects that have a cleanup phase:
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
}, [props.friend.id]); // Only re-subscribe if props.friend.id changes
In the future, the second argument might get added automatically by a build-time transformation.
Note
If you use this optimization, make sure the array includes all values from the component scope (such as props and state) that change over time and that are used by the effect . Otherwise, your code will reference stale values from previous renders. Learn more about how to deal with functions and what to do when the array changes too often.
If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array (
[]
) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. This isn’t handled as a special case — it follows directly from how the dependencies array always works.
If you pass an empty array (
[]
), the props and state inside the effect will always have their initial values. While passing[]
as the second argument is closer to the familiarcomponentDidMount
andcomponentWillUnmount
mental model, there are usually better solutions to avoid re-running effects too often. Also, don’t forget that React defers runninguseEffect
until after the browser has painted, so doing extra work is less of a problem.
We recommend using the
exhaustive-deps
rule as part of oureslint-plugin-react-hooks
package. It warns when dependencies are specified incorrectly and suggests a fix.
Congratulations! This was a long page, but hopefully by the end most of your questions about effects were answered. You’ve learned both the State Hook and the Effect Hook, and there is a lot you can do with both of them combined. They cover most of the use cases for classes — and where they don’t, you might find the additional Hooks helpful.
We’re also starting to see how Hooks solve problems outlined in Motivation. We’ve seen how effect cleanup avoids duplication in
componentDidUpdate
and
componentWillUnmount
, brings related code closer together, and helps us avoid bugs. We’ve also seen how we can separate effects by their purpose, which is something we couldn’t do in classes at all.
At this point you might be questioning how Hooks work. How can React know which
useState
call corresponds to which state variable between re-renders? How does React “match up” previous and next effects on every update?
On the next page we will learn about the Rules of Hooks — they’re essential to making Hooks work.
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
Hooks are JavaScript functions, but you need to follow two rules when using them. We provide a linter plugin to enforce these rules automatically:
Don’t call Hooks inside loops, conditions, or nested functions.
Instead, always use Hooks at the top level of your React function, before any early returns. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple
useState
and
useEffect
calls. (If you’re curious, we’ll explain this in depth below.)
Don’t call Hooks from regular JavaScript functions. Instead, you can:
By following this rule, you ensure that all stateful logic in a component is clearly visible from its source code.
We released an ESLint plugin called
eslint-plugin-react-hooks
that enforces these two rules. You can add this plugin to your project if you’d like to try it:
This plugin is included by default in Create React App.
npm install eslint-plugin-react-hooks --save-dev
// Your ESLint configuration
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
"react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
}
}
You can skip to the next page explaining how to write your own Hooks now. On this page, we’ll continue by explaining the reasoning behind these rules.
As we learned earlier, we can use multiple State or Effect Hooks in a single component:
function Form() {
// 1. Use the name state variable
const [name, setName] = useState('Mary');
// 2. Use an effect for persisting the form
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
// 3. Use the surname state variable
const [surname, setSurname] = useState('Poppins');
// 4. Use an effect for updating the title
useEffect(function updateTitle() {
document.title = name + ' ' + surname;
});
// ...
}
So how does React know which state corresponds to which
useState
call? The answer is that
React relies on the order in which Hooks are called
. Our example works because the order of the Hook calls is the same on every render:
// ------------
// First render
// ------------
useState('Mary') // 1. Initialize the name state variable with 'Mary'
useEffect(persistForm) // 2. Add an effect for persisting the form
useState('Poppins') // 3. Initialize the surname state variable with 'Poppins'
useEffect(updateTitle) // 4. Add an effect for updating the title
// -------------
// Second render
// -------------
useState('Mary') // 1. Read the name state variable (argument is ignored)
useEffect(persistForm) // 2. Replace the effect for persisting the form
useState('Poppins') // 3. Read the surname state variable (argument is ignored)
useEffect(updateTitle) // 4. Replace the effect for updating the title
// ...
As long as the order of the Hook calls is the same between renders, React can associate some local state with each of them. But what happens if we put a Hook call (for example, the
persistForm
effect) inside a condition?
// 🔴 We're breaking the first rule by using a Hook in a condition
if (name !== '') {
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
}
The
name !== ''
condition is
true
on the first render, so we run this Hook. However, on the next render the user might clear the form, making the condition
false
. Now that we skip this Hook during rendering, the order of the Hook calls becomes different:
useState('Mary') // 1. Read the name state variable (argument is ignored)
// useEffect(persistForm) // 🔴 This Hook was skipped!
useState('Poppins') // 🔴 2 (but was 3). Fail to read the surname state variable
useEffect(updateTitle) // 🔴 3 (but was 4). Fail to replace the effect
React wouldn’t know what to return for the second
useState
Hook call. React expected that the second Hook call in this component corresponds to the
persistForm
effect, just like during the previous render, but it doesn’t anymore. From that point, every next Hook call after the one we skipped would also shift by one, leading to bugs.
This is why Hooks must be called on the top level of our components. If we want to run an effect conditionally, we can put that condition inside our Hook:
useEffect(function persistForm() {
// 👍 We're not breaking the first rule anymore
if (name !== '') {
localStorage.setItem('formData', name);
}
});
Note that you don’t need to worry about this problem if you use the provided lint rule. But now you also know why Hooks work this way, and which issues the rule is preventing.
Finally, we’re ready to learn about writing your own Hooks! Custom Hooks let you combine Hooks provided by React into your own abstractions, and reuse common stateful logic between different components.
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
Building your own Hooks lets you extract component logic into reusable functions.
When we were learning about using the Effect Hook, we saw this component from a chat application that displays a message indicating whether a friend is online or offline:
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; });
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
Now let’s say that our chat application also has a contact list, and we want to render names of online users with a green color. We could copy and paste similar logic above into our
FriendListItem
component but it wouldn’t be ideal:
import React, { useState, useEffect } from 'react';
function FriendListItem(props) {
const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; });
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.friend.name}
</li>
);
}
Instead, we’d like to share this logic between
FriendStatus
and
FriendListItem
.
Traditionally in React, we’ve had two popular ways to share stateful logic between components: render props and higher-order components. We will now look at how Hooks solve many of the same problems without forcing you to add more components to the tree.
When we want to share logic between two JavaScript functions, we extract it to a third function. Both components and Hooks are functions, so this works for them too!
A custom Hook is a JavaScript function whose name starts with ”
use
” and that may call other Hooks.
For example,
useFriendStatus
below is our first custom Hook:
import { useState, useEffect } from 'react';
function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
There’s nothing new inside of it — the logic is copied from the components above. Just like in a component, make sure to only call other Hooks unconditionally at the top level of your custom Hook.
Unlike a React component, a custom Hook doesn’t need to have a specific signature. We can decide what it takes as arguments, and what, if anything, it should return. In other words, it’s just like a normal function. Its name should always start with
use
so that you can tell at a glance that the rules of Hooks apply to it.
The purpose of our
useFriendStatus
Hook is to subscribe us to a friend’s status. This is why it takes
friendID
as an argument, and returns whether this friend is online:
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
// ...
return isOnline;
}
Now let’s see how we can use our custom Hook.
In the beginning, our stated goal was to remove the duplicated logic from the
FriendStatus
and
FriendListItem
components. Both of them want to know whether a friend is online.
Now that we’ve extracted this logic to a
useFriendStatus
hook, we can
just use it:
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
function FriendListItem(props) {
const isOnline = useFriendStatus(props.friend.id);
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.friend.name}
</li>
);
}
Is this code equivalent to the original examples? Yes, it works in exactly the same way. If you look closely, you’ll notice we didn’t make any changes to the behavior. All we did was to extract some common code between two functions into a separate function. Custom Hooks are a convention that naturally follows from the design of Hooks, rather than a React feature.
Do I have to name my custom Hooks starting with “
use
”?
Please do. This convention is very important. Without it, we wouldn’t be able to automatically check for violations of rules of Hooks because we couldn’t tell if a certain function contains calls to Hooks inside of it.
Do two components using the same Hook share state? No. Custom Hooks are a mechanism to reuse stateful logic (such as setting up a subscription and remembering the current value), but every time you use a custom Hook, all state and effects inside of it are fully isolated.
How does a custom Hook get isolated state?
Each
call
to a Hook gets isolated state. Because we call
useFriendStatus
directly, from React’s point of view our component just calls
useState
and
useEffect
. And as we learned earlier, we can call
useState
and
useEffect
many times in one component, and they will be completely independent.
Since Hooks are functions, we can pass information between them.
To illustrate this, we’ll use another component from our hypothetical chat example. This is a chat message recipient picker that displays whether the currently selected friend is online:
const friendList = [
{ id: 1, name: 'Phoebe' },
{ id: 2, name: 'Rachel' },
{ id: 3, name: 'Ross' },
];
function ChatRecipientPicker() {
const [recipientID, setRecipientID] = useState(1); const isRecipientOnline = useFriendStatus(recipientID);
return (
<>
<Circle color={isRecipientOnline ? 'green' : 'red'} /> <select
value={recipientID}
onChange={e => setRecipientID(Number(e.target.value))}
>
{friendList.map(friend => (
<option key={friend.id} value={friend.id}>
{friend.name}
</option>
))}
</select>
</>
);
}
We keep the currently chosen friend ID in the
recipientID
state variable, and update it if the user chooses a different friend in the
<select>
picker.
Because the
useState
Hook call gives us the latest value of the
recipientID
state variable, we can pass it to our custom
useFriendStatus
Hook as an argument:
const [recipientID, setRecipientID] = useState(1);
const isRecipientOnline = useFriendStatus(recipientID);
This lets us know whether the
currently selected
friend is online. If we pick a different friend and update the
recipientID
state variable, our
useFriendStatus
Hook will unsubscribe from the previously selected friend, and subscribe to the status of the newly selected one.
useYourImagination()
Custom Hooks offer the flexibility of sharing logic that wasn’t possible in React components before. You can write custom Hooks that cover a wide range of use cases like form handling, animation, declarative subscriptions, timers, and probably many more we haven’t considered. What’s more, you can build Hooks that are just as easy to use as React’s built-in features.
Try to resist adding abstraction too early. Now that function components can do more, it’s likely that the average function component in your codebase will become longer. This is normal — don’t feel like you have to immediately split it into Hooks. But we also encourage you to start spotting cases where a custom Hook could hide complex logic behind a simple interface, or help untangle a messy component.
For example, maybe you have a complex component that contains a lot of local state that is managed in an ad-hoc way.
useState
doesn’t make centralizing the update logic any easier so you might prefer to write it as a Redux reducer:
function todosReducer(state, action) {
switch (action.type) {
case 'add':
return [...state, {
text: action.text,
completed: false
}];
// ... other actions ...
default:
return state;
}
}
Reducers are very convenient to test in isolation, and scale to express complex update logic. You can further break them apart into smaller reducers if necessary. However, you might also enjoy the benefits of using React local state, or might not want to install another library.
So what if we could write a
useReducer
Hook that lets us manage the
local
state of our component with a reducer? A simplified version of it might look like this:
function useReducer(reducer, initialState) {
const [state, setState] = useState(initialState);
function dispatch(action) {
const nextState = reducer(state, action);
setState(nextState);
}
return [state, dispatch];
}
Now we could use it in our component, and let the reducer drive its state management:
function Todos() {
const [todos, dispatch] = useReducer(todosReducer, []);
function handleAddClick(text) {
dispatch({ type: 'add', text });
}
// ...
}
The need to manage local state with a reducer in a complex component is common enough that we’ve built the
useReducer
Hook right into React. You’ll find it together with other built-in Hooks in the Hooks API reference.
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
This page describes the APIs for the built-in Hooks in React.
If you’re new to Hooks, you might want to check out the overview first. You may also find useful information in the frequently asked questions section.
Basic Hooks
useState
useEffect
useContext
Additional Hooks
useReducer
useCallback
useMemo
useRef
useImperativeHandle
useLayoutEffect
useDebugValue
useDeferredValue
useTransition
useId
Library Hooks
useSyncExternalStore
useInsertionEffect
useState
const [state, setState] = useState(initialState);
Returns a stateful value, and a function to update it.
During the initial render, the returned state (
state
) is the same as the value passed as the first argument (
initialState
).
The
setState
function is used to update the state. It accepts a new state value and enqueues a re-render of the component.
setState(newState);
During subsequent re-renders, the first value returned by
useState
will always be the most recent state after applying updates.
Note
React guarantees that
setState
function identity is stable and won’t change on re-renders. This is why it’s safe to omit from theuseEffect
oruseCallback
dependency list.
If the new state is computed using the previous state, you can pass a function to
setState
. The function will receive the previous value, and return an updated value. Here’s an example of a counter component that uses both forms of
setState
:
function Counter({initialCount}) {
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
</>
);
}
The ”+” and ”-” buttons use the functional form, because the updated value is based on the previous value. But the “Reset” button uses the normal form, because it always sets the count back to the initial value.
If your update function returns the exact same value as the current state, the subsequent rerender will be skipped completely.
Note
Unlike the
setState
method found in class components,useState
does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax:
const [state, setState] = useState({});
setState(prevState => {
// Object.assign would also work
return {...prevState, ...updatedValues};
});
Another option is
useReducer
, which is more suited for managing state objects that contain multiple sub-values.
The
initialState
argument is the state used during the initial render. In subsequent renders, it is disregarded. If the initial state is the result of an expensive computation, you may provide a function instead, which will be executed only on the initial render:
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the
Object.is
comparison algorithm.)
Note that React may still need to render that specific component again before bailing out. That shouldn’t be a concern because React won’t unnecessarily go “deeper” into the tree. If you’re doing expensive calculations while rendering, you can optimize them with
useMemo
.
React may group several state updates into a single re-render to improve performance. Normally, this improves performance and shouldn’t affect your application’s behavior.
Before React 18, only updates inside React event handlers were batched. Starting with React 18, batching is enabled for all updates by default. Note that React makes sure that updates from several different user-initiated events — for example, clicking a button twice — are always processed separately and do not get batched. This prevents logical mistakes.
In the rare case that you need to force the DOM update to be applied synchronously, you may wrap it in
flushSync
. However, this can hurt performance so do this only where needed.
useEffect
useEffect(didUpdate);
Accepts a function that contains imperative, possibly effectful code.
Mutations, subscriptions, timers, logging, and other side effects are not allowed inside the main body of a function component (referred to as React’s render phase ). Doing so will lead to confusing bugs and inconsistencies in the UI.
Instead, use
useEffect
. The function passed to
useEffect
will run after the render is committed to the screen. Think of effects as an escape hatch from React’s purely functional world into the imperative world.
By default, effects run after every completed render, but you can choose to fire them only when certain values have changed.
Often, effects create resources that need to be cleaned up before the component leaves the screen, such as a subscription or timer ID. To do this, the function passed to
useEffect
may return a clean-up function. For example, to create a subscription:
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// Clean up the subscription
subscription.unsubscribe();
};
});
The clean-up function runs before the component is removed from the UI to prevent memory leaks. Additionally, if a component renders multiple times (as they typically do), the previous effect is cleaned up before executing the next effect . In our example, this means a new subscription is created on every update. To avoid firing an effect on every update, refer to the next section.
Unlike
componentDidMount
and
componentDidUpdate
, the function passed to
useEffect
fires
after
layout and paint, during a deferred event. This makes it suitable for the many common side effects, like setting up subscriptions and event handlers, because most types of work shouldn’t block the browser from updating the screen.
However, not all effects can be deferred. For example, a DOM mutation that is visible to the user must fire synchronously before the next paint so that the user does not perceive a visual inconsistency. (The distinction is conceptually similar to passive versus active event listeners.) For these types of effects, React provides one additional Hook called
useLayoutEffect
. It has the same signature as
useEffect
, and only differs in when it is fired.
Additionally, starting in React 18, the function passed to
useEffect
will fire synchronously
before
layout and paint when it’s the result of a discrete user input such as a click, or when it’s the result of an update wrapped in
flushSync
. This behavior allows the result of the effect to be observed by the event system, or by the caller of
flushSync
.
Note
This only affects the timing of when the function passed to
useEffect
is called - updates scheduled inside these effects are still deferred. This is different thanuseLayoutEffect
, which fires the function and processes the updates inside of it immediately.
Even in cases where
useEffect
is deferred until after the browser has painted, it’s guaranteed to fire before any new renders. React will always flush a previous render’s effects before starting a new update.
The default behavior for effects is to fire the effect after every completed render. That way an effect is always recreated if one of its dependencies changes.
However, this may be overkill in some cases, like the subscription example from the previous section. We don’t need to create a new subscription on every update, only if the
source
prop has changed.
To implement this, pass a second argument to
useEffect
that is the array of values that the effect depends on. Our updated example now looks like this:
useEffect(
() => {
const subscription = props.source.subscribe();
return () => {
subscription.unsubscribe();
};
},
[props.source],
);
Now the subscription will only be recreated when
props.source
changes.
Note
If you use this optimization, make sure the array includes all values from the component scope (such as props and state) that change over time and that are used by the effect . Otherwise, your code will reference stale values from previous renders. Learn more about how to deal with functions and what to do when the array values change too often.
If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array (
[]
) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. This isn’t handled as a special case — it follows directly from how the dependencies array always works.
If you pass an empty array (
[]
), the props and state inside the effect will always have their initial values. While passing[]
as the second argument is closer to the familiarcomponentDidMount
andcomponentWillUnmount
mental model, there are usually better solutions to avoid re-running effects too often. Also, don’t forget that React defers runninguseEffect
until after the browser has painted, so doing extra work is less of a problem.
We recommend using the
exhaustive-deps
rule as part of oureslint-plugin-react-hooks
package. It warns when dependencies are specified incorrectly and suggests a fix.
The array of dependencies is not passed as arguments to the effect function. Conceptually, though, that’s what they represent: every value referenced inside the effect function should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically.
useContext
const value = useContext(MyContext);
Accepts a context object (the value returned from
React.createContext
) and returns the current context value for that context. The current context value is determined by the
value
prop of the nearest
<MyContext.Provider>
above the calling component in the tree.
When the nearest
<MyContext.Provider>
above the component updates, this Hook will trigger a rerender with the latest context
value
passed to that
MyContext
provider. Even if an ancestor uses
React.memo
or
shouldComponentUpdate
, a rerender will still happen starting at the component itself using
useContext
.
Don’t forget that the argument to
useContext
must be the
context object itself
:
useContext(MyContext)
useContext(MyContext.Consumer)
useContext(MyContext.Provider)
A component calling
useContext
will always re-render when the context value changes. If re-rendering the component is expensive, you can optimize it by using memoization.
Tip
If you’re familiar with the context API before Hooks,
useContext(MyContext)
is equivalent tostatic contextType = MyContext
in a class, or to<MyContext.Consumer>
.
useContext(MyContext)
only lets you read the context and subscribe to its changes. You still need a<MyContext.Provider>
above in the tree to provide the value for this context.
Putting it together with Context.Provider
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext); return ( <button style={{ background: theme.background, color: theme.foreground }}> I am styled by theme context! </button> );
}
This example is modified for hooks from a previous example in the Context Advanced Guide, where you can find more information about when and how to use Context.
The following Hooks are either variants of the basic ones from the previous section, or only needed for specific edge cases. Don’t stress about learning them up front.
useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init);
An alternative to
useState
. Accepts a reducer of type
(state, action) => newState
, and returns the current state paired with a
dispatch
method. (If you’re familiar with Redux, you already know how this works.)
useReducer
is usually preferable to
useState
when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one.
useReducer
also lets you optimize performance for components that trigger deep updates because you can pass
dispatch
down instead of callbacks.
Here’s the counter example from the
useState
section, rewritten to use a reducer:
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
Note
React guarantees that
dispatch
function identity is stable and won’t change on re-renders. This is why it’s safe to omit from theuseEffect
oruseCallback
dependency list.
There are two different ways to initialize
useReducer
state. You may choose either one depending on the use case. The simplest way is to pass the initial state as a second argument:
const [state, dispatch] = useReducer(
reducer,
{count: initialCount} );
Note
React doesn’t use the
state = initialState
argument convention popularized by Redux. The initial value sometimes needs to depend on props and so is specified from the Hook call instead. If you feel strongly about this, you can calluseReducer(reducer, undefined, reducer)
to emulate the Redux behavior, but it’s not encouraged.
You can also create the initial state lazily. To do this, you can pass an
init
function as the third argument. The initial state will be set to
init(initialArg)
.
It lets you extract the logic for calculating the initial state outside the reducer. This is also handy for resetting the state later in response to an action:
function init(initialCount) { return {count: initialCount};}
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
case 'reset': return init(action.payload); default:
throw new Error();
}
}
function Counter({initialCount}) {
const [state, dispatch] = useReducer(reducer, initialCount, init); return (
<>
Count: {state.count}
<button
onClick={() => dispatch({type: 'reset', payload: initialCount})}> Reset
</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
If you return the same value from a Reducer Hook as the current state, React will bail out without rendering the children or firing effects. (React uses the
Object.is
comparison algorithm.)
Note that React may still need to render that specific component again before bailing out. That shouldn’t be a concern because React won’t unnecessarily go “deeper” into the tree. If you’re doing expensive calculations while rendering, you can optimize them with
useMemo
.
useCallback
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
Returns a memoized callback.
Pass an inline callback and an array of dependencies.
useCallback
will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g.
shouldComponentUpdate
).
useCallback(fn, deps)
is equivalent to
useMemo(() => fn, deps)
.
Note
The array of dependencies is not passed as arguments to the callback. Conceptually, though, that’s what they represent: every value referenced inside the callback should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically.
We recommend using the
exhaustive-deps
rule as part of oureslint-plugin-react-hooks
package. It warns when dependencies are specified incorrectly and suggests a fix.
useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
Returns a memoized value.
Pass a “create” function and an array of dependencies.
useMemo
will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.
Remember that the function passed to
useMemo
runs during rendering. Don’t do anything there that you wouldn’t normally do while rendering. For example, side effects belong in
useEffect
, not
useMemo
.
If no array is provided, a new value will be computed on every render.
You may rely on
useMemo
as a performance optimization, not as a semantic guarantee.
In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without
useMemo
— and then add it to optimize performance.
Note
The array of dependencies is not passed as arguments to the function. Conceptually, though, that’s what they represent: every value referenced inside the function should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically.
We recommend using the
exhaustive-deps
rule as part of oureslint-plugin-react-hooks
package. It warns when dependencies are specified incorrectly and suggests a fix.
useRef
const refContainer = useRef(initialValue);
useRef
returns a mutable ref object whose
.current
property is initialized to the passed argument (
initialValue
). The returned object will persist for the full lifetime of the component.
A common use case is to access a child imperatively:
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
Essentially,
useRef
is like a “box” that can hold a mutable value in its
.current
property.
You might be familiar with refs primarily as a way to access the DOM. If you pass a ref object to React with
<div ref={myRef} />
, React will set its
.current
property to the corresponding DOM node whenever that node changes.
However,
useRef()
is useful for more than the
ref
attribute. It’s handy for keeping any mutable value around similar to how you’d use instance fields in classes.
This works because
useRef()
creates a plain JavaScript object. The only difference between
useRef()
and creating a
{current: ...}
object yourself is that
useRef
will give you the same ref object on every render.
Keep in mind that
useRef
doesn’t
notify you when its content changes. Mutating the
.current
property doesn’t cause a re-render. If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a callback ref instead.
useImperativeHandle
useImperativeHandle(ref, createHandle, [deps])
useImperativeHandle
customizes the instance value that is exposed to parent components when using
ref
. As always, imperative code using refs should be avoided in most cases.
useImperativeHandle
should be used with
forwardRef
:
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
In this example, a parent component that renders
<FancyInput ref={inputRef} />
would be able to call
inputRef.current.focus()
.
useLayoutEffect
The signature is identical to
useEffect
, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside
useLayoutEffect
will be flushed synchronously, before the browser has a chance to paint.
Prefer the standard
useEffect
when possible to avoid blocking visual updates.
Tip
If you’re migrating code from a class component, note
useLayoutEffect
fires in the same phase ascomponentDidMount
andcomponentDidUpdate
. However, we recommend starting withuseEffect
first and only tryinguseLayoutEffect
if that causes a problem.
If you use server rendering, keep in mind that neither
useLayoutEffect
noruseEffect
can run until the JavaScript is downloaded. This is why React warns when a server-rendered component containsuseLayoutEffect
. To fix this, either move that logic touseEffect
(if it isn’t necessary for the first render), or delay showing that component until after the client renders (if the HTML looks broken untiluseLayoutEffect
runs).
To exclude a component that needs layout effects from the server-rendered HTML, render it conditionally with
showChild && <Child />
and defer showing it withuseEffect(() => { setShowChild(true); }, [])
. This way, the UI doesn’t appear broken before hydration.
useDebugValue
useDebugValue(value)
useDebugValue
can be used to display a label for custom hooks in React DevTools.
For example, consider the
useFriendStatus
custom Hook described in “Building Your Own Hooks”:
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
// ...
// Show a label in DevTools next to this Hook // e.g. "FriendStatus: Online" useDebugValue(isOnline ? 'Online' : 'Offline');
return isOnline;
}
Tip
We don’t recommend adding debug values to every custom Hook. It’s most valuable for custom Hooks that are part of shared libraries.
In some cases formatting a value for display might be an expensive operation. It’s also unnecessary unless a Hook is actually inspected.
For this reason
useDebugValue
accepts a formatting function as an optional second parameter. This function is only called if the Hooks are inspected. It receives the debug value as a parameter and should return a formatted display value.
For example a custom Hook that returned a
Date
value could avoid calling the
toDateString
function unnecessarily by passing the following formatter:
useDebugValue(date, date => date.toDateString());
useDeferredValue
const deferredValue = useDeferredValue(value);
useDeferredValue
accepts a value and returns a new copy of the value that will defer to more urgent updates. If the current render is the result of an urgent update, like user input, React will return the previous value and then render the new value after the urgent render has completed.
This hook is similar to user-space hooks which use debouncing or throttling to defer updates. The benefits to using
useDeferredValue
is that React will work on the update as soon as other work finishes (instead of waiting for an arbitrary amount of time), and like
startTransition
, deferred values can suspend without triggering an unexpected fallback for existing content.
useDeferredValue
only defers the value that you pass to it. If you want to prevent a child component from re-rendering during an urgent update, you must also memoize that component with
React.memo
or
React.useMemo
:
function Typeahead() {
const query = useSearchQuery('');
const deferredQuery = useDeferredValue(query);
// Memoizing tells React to only re-render when deferredQuery changes,
// not when query changes.
const suggestions = useMemo(() =>
<SearchSuggestions query={deferredQuery} />,
[deferredQuery]
);
return (
<>
<SearchInput query={query} />
<Suspense fallback="Loading results...">
{suggestions}
</Suspense>
</>
);
}
Memoizing the children tells React that it only needs to re-render them when
deferredQuery
changes and not when
query
changes. This caveat is not unique to
useDeferredValue
, and it’s the same pattern you would use with similar hooks that use debouncing or throttling.
useTransition
const [isPending, startTransition] = useTransition();
Returns a stateful value for the pending state of the transition, and a function to start it.
startTransition
lets you mark updates in the provided callback as transitions:
startTransition(() => {
setCount(count + 1);
});
isPending
indicates when a transition is active to show a pending state:
function App() {
const [isPending, startTransition] = useTransition();
const [count, setCount] = useState(0);
function handleClick() {
startTransition(() => {
setCount(c => c + 1);
});
}
return (
<div>
{isPending && <Spinner />}
<button onClick={handleClick}>{count}</button>
</div>
);
}
Note:
Updates in a transition yield to more urgent updates such as clicks.
Updates in a transition will not show a fallback for re-suspended content. This allows the user to continue interacting with the current content while rendering the update.
useId
const id = useId();
useId
is a hook for generating unique IDs that are stable across the server and client, while avoiding hydration mismatches.
Note
useId
is not for generating keys in a list. Keys should be generated from your data.
For a basic example, pass the
id
directly to the elements that need it:
function Checkbox() {
const id = useId();
return (
<>
<label htmlFor={id}>Do you like React?</label>
<input id={id} type="checkbox" name="react"/>
</>
);
};
For multiple IDs in the same component, append a suffix using the same
id
:
function NameFields() {
const id = useId();
return (
<div>
<label htmlFor={id + '-firstName'}>First Name</label>
<div>
<input id={id + '-firstName'} type="text" />
</div>
<label htmlFor={id + '-lastName'}>Last Name</label>
<div>
<input id={id + '-lastName'} type="text" />
</div>
</div>
);
}
Note:
useId
generates a string that includes the:
token. This helps ensure that the token is unique, but is not supported in CSS selectors or APIs likequerySelectorAll
.
useId
supports anidentifierPrefix
to prevent collisions in multi-root apps. To configure, see the options forhydrateRoot
andReactDOMServer
.
The following Hooks are provided for library authors to integrate libraries deeply into the React model, and are not typically used in application code.
useSyncExternalStore
const state = useSyncExternalStore(subscribe, getSnapshot[, getServerSnapshot]);
useSyncExternalStore
is a hook recommended for reading and subscribing from external data sources in a way that’s compatible with concurrent rendering features like selective hydration and time slicing.
This method returns the value of the store and accepts three arguments:
subscribe
: function to register a callback that is called whenever the store changes.
getSnapshot
: function that returns the current value of the store.
getServerSnapshot
: function that returns the snapshot used during server rendering.
The most basic example simply subscribes to the entire store:
const state = useSyncExternalStore(store.subscribe, store.getSnapshot);
However, you can also subscribe to a specific field:
const selectedField = useSyncExternalStore(
store.subscribe,
() => store.getSnapshot().selectedField,
);
When server rendering, you must serialize the store value used on the server, and provide it to
useSyncExternalStore
. React will use this snapshot during hydration to prevent server mismatches:
const selectedField = useSyncExternalStore(
store.subscribe,
() => store.getSnapshot().selectedField,
() => INITIAL_SERVER_SNAPSHOT.selectedField,
);
Note:
getSnapshot
must return a cached value. If getSnapshot is called multiple times in a row, it must return the same exact value unless there was a store update in between.
A shim is provided for supporting multiple React versions published as
use-sync-external-store/shim
. This shim will preferuseSyncExternalStore
when available, and fallback to a user-space implementation when it’s not.
As a convenience, we also provide a version of the API with automatic support for memoizing the result of getSnapshot published as
use-sync-external-store/with-selector
.
useInsertionEffect
useInsertionEffect(didUpdate);
The signature is identical to
useEffect
, but it fires synchronously
before
all DOM mutations. Use this to inject styles into the DOM before reading layout in
useLayoutEffect
. Since this hook is limited in scope, this hook does not have access to refs and cannot schedule updates.
Note:
useInsertionEffect
should be limited to css-in-js library authors. PreferuseEffect
oruseLayoutEffect
instead.
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
This page answers some of the frequently asked questions about Hooks.
Adoption Strategy
From Classes to Hooks
Performance Optimizations
Under the Hood
Starting with 16.8.0, React includes a stable implementation of React Hooks for:
Note that to enable Hooks, all React packages need to be 16.8.0 or higher . Hooks won’t work if you forget to update, for example, React DOM.
React Native 0.59 and above support Hooks.
No. There are no plans to remove classes from React — we all need to keep shipping products and can’t afford rewrites. We recommend trying Hooks in new code.
Hooks offer a powerful and expressive new way to reuse functionality between components. “Building Your Own Hooks” provides a glimpse of what’s possible. This article by a React core team member dives deeper into the new capabilities unlocked by Hooks.
Hooks are a more direct way to use the React features you already know — such as state, lifecycle, context, and refs. They don’t fundamentally change how React works, and your knowledge of components, props, and top-down data flow is just as relevant.
Hooks do have a learning curve of their own. If there’s something missing in this documentation, raise an issue and we’ll try to help.
When you’re ready, we’d encourage you to start trying Hooks in new components you write. Make sure everyone on your team is on board with using them and familiar with this documentation. We don’t recommend rewriting your existing classes to Hooks unless you planned to rewrite them anyway (e.g. to fix bugs).
You can’t use Hooks inside a class component, but you can definitely mix classes and function components with Hooks in a single tree. Whether a component is a class or a function that uses Hooks is an implementation detail of that component. In the longer term, we expect Hooks to be the primary way people write React components.
Our goal is for Hooks to cover all use cases for classes as soon as possible. There are no Hook equivalents to the uncommon
getSnapshotBeforeUpdate
,
getDerivedStateFromError
and
componentDidCatch
lifecycles yet, but we plan to add them soon.
Often, render props and higher-order components render only a single child. We think Hooks are a simpler way to serve this use case. There is still a place for both patterns (for example, a virtual scroller component might have a
renderItem
prop, or a visual container component might have its own DOM structure). But in most cases, Hooks will be sufficient and can help reduce nesting in your tree.
connect()
and React Router?
You can continue to use the exact same APIs as you always have; they’ll continue to work.
React Redux since v7.1.0 supports Hooks API and exposes hooks like
useDispatch
or
useSelector
.
React Router supports hooks since v5.1.
Other libraries might support hooks in the future too.
Hooks were designed with static typing in mind. Because they’re functions, they are easier to type correctly than patterns like higher-order components. The latest Flow and TypeScript React definitions include support for React Hooks.
Importantly, custom Hooks give you the power to constrain React API if you’d like to type them more strictly in some way. React gives you the primitives, but you can combine them in different ways than what we provide out of the box.
From React’s point of view, a component using Hooks is just a regular component. If your testing solution doesn’t rely on React internals, testing components with Hooks shouldn’t be different from how you normally test components.
Note
Testing Recipes include many examples that you can copy and paste.
For example, let’s say we have this counter component:
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
We’ll test it using React DOM. To make sure that the behavior matches what happens in the browser, we’ll wrap the code rendering and updating it into
ReactTestUtils.act()
calls:
import React from 'react';
import ReactDOM from 'react-dom/client';
import { act } from 'react-dom/test-utils';import Counter from './Counter';
let container;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
container = null;
});
it('can render and update a counter', () => {
// Test first render and effect
act(() => { ReactDOM.createRoot(container).render(<Counter />); }); const button = container.querySelector('button');
const label = container.querySelector('p');
expect(label.textContent).toBe('You clicked 0 times');
expect(document.title).toBe('You clicked 0 times');
// Test second render and effect
act(() => { button.dispatchEvent(new MouseEvent('click', {bubbles: true})); }); expect(label.textContent).toBe('You clicked 1 times');
expect(document.title).toBe('You clicked 1 times');
});
The calls to
act()
will also flush the effects inside of them.
If you need to test a custom Hook, you can do so by creating a component in your test, and using your Hook from it. Then you can test the component you wrote.
To reduce the boilerplate, we recommend using React Testing Library which is designed to encourage writing tests that use your components as the end users do.
For more information, check out Testing Recipes.
We provide an ESLint plugin that enforces rules of Hooks to avoid bugs. It assumes that any function starting with ”
use
” and a capital letter right after it is a Hook. We recognize this heuristic isn’t perfect and there may be some false positives, but without an ecosystem-wide convention there is just no way to make Hooks work well — and longer names will discourage people from either adopting Hooks or following the convention.
In particular, the rule enforces that:
PascalCase
function (assumed to be a component) or another
useSomething
function (assumed to be a custom Hook).
There are a few more heuristics, and they might change over time as we fine-tune the rule to balance finding bugs with avoiding false positives.
constructor
: Function components don’t need a constructor. You can initialize the state in the
useState
call. If computing the initial state is expensive, you can pass a function to
useState
.
getDerivedStateFromProps
: Schedule an update while rendering instead.
shouldComponentUpdate
: See
React.memo
below.
render
: This is the function component body itself.
componentDidMount
,
componentDidUpdate
,
componentWillUnmount
: The
useEffect
Hook can express all combinations of these (including less common cases).
getSnapshotBeforeUpdate
,
componentDidCatch
and
getDerivedStateFromError
: There are no Hook equivalents for these methods yet, but they will be added soon.
Here is a small demo to get you started. To learn more, check out this article about data fetching with Hooks.
Yes! The
useRef()
Hook isn’t just for DOM refs. The “ref” object is a generic container whose
current
property is mutable and can hold any value, similar to an instance property on a class.
You can write to it from inside
useEffect
:
function Timer() {
const intervalRef = useRef();
useEffect(() => {
const id = setInterval(() => {
// ...
});
intervalRef.current = id; return () => {
clearInterval(intervalRef.current);
};
});
// ...
}
If we just wanted to set an interval, we wouldn’t need the ref (
id
could be local to the effect), but it’s useful if we want to clear the interval from an event handler:
// ...
function handleCancelClick() {
clearInterval(intervalRef.current); }
// ...
Conceptually, you can think of refs as similar to instance variables in a class. Unless you’re doing lazy initialization, avoid setting refs during rendering — this can lead to surprising behavior. Instead, typically you want to modify refs in event handlers and effects.
If you’re coming from classes, you might be tempted to always call
useState()
once and put all state into a single object. You can do it if you’d like. Here is an example of a component that follows the mouse movement. We keep its position and size in the local state:
function Box() {
const [state, setState] = useState({ left: 0, top: 0, width: 100, height: 100 });
// ...
}
Now let’s say we want to write some logic that changes
left
and
top
when the user moves their mouse. Note how we have to merge these fields into the previous state object manually:
// ...
useEffect(() => {
function handleWindowMouseMove(e) {
// Spreading "...state" ensures we don't "lose" width and height setState(state => ({ ...state, left: e.pageX, top: e.pageY })); }
// Note: this implementation is a bit simplified
window.addEventListener('mousemove', handleWindowMouseMove);
return () => window.removeEventListener('mousemove', handleWindowMouseMove);
}, []);
// ...
This is because when we update a state variable, we
replace
its value. This is different from
this.setState
in a class, which
merges
the updated fields into the object.
If you miss automatic merging, you could write a custom
useLegacyState
Hook that merges object state updates. However,
we recommend to split state into multiple state variables based on which values tend to change together.
For example, we could split our component state into
position
and
size
objects, and always replace the
position
with no need for merging:
function Box() {
const [position, setPosition] = useState({ left: 0, top: 0 }); const [size, setSize] = useState({ width: 100, height: 100 });
useEffect(() => {
function handleWindowMouseMove(e) {
setPosition({ left: e.pageX, top: e.pageY }); }
// ...
Separating independent state variables also has another benefit. It makes it easy to later extract some related logic into a custom Hook, for example:
function Box() {
const position = useWindowPosition(); const [size, setSize] = useState({ width: 100, height: 100 });
// ...
}
function useWindowPosition() { const [position, setPosition] = useState({ left: 0, top: 0 });
useEffect(() => {
// ...
}, []);
return position;
}
Note how we were able to move the
useState
call for the
position
state variable and the related effect into a custom Hook without changing their code. If all state was in a single object, extracting it would be more difficult.
Both putting all state in a single
useState
call, and having a
useState
call per each field can work. Components tend to be most readable when you find a balance between these two extremes, and group related state into a few independent state variables. If the state logic becomes complex, we recommend managing it with a reducer or a custom Hook.
This is a rare use case. If you need it, you can use a mutable ref to manually store a boolean value corresponding to whether you are on the first or a subsequent render, then check that flag in your effect. (If you find yourself doing this often, you could create a custom Hook for it.)
There are two cases in which you might want to get previous props or state.
Sometimes, you need previous props to
clean up an effect.
For example, you might have an effect that subscribes to a socket based on the
userId
prop. If the
userId
prop changes, you want to unsubscribe from the
previous
userId
and subscribe to the
next
one. You don’t need to do anything special for this to work:
useEffect(() => {
ChatAPI.subscribeToSocket(props.userId);
return () => ChatAPI.unsubscribeFromSocket(props.userId);
}, [props.userId]);
In the above example, if
userId
changes from
3
to
4
,
ChatAPI.unsubscribeFromSocket(3)
will run first, and then
ChatAPI.subscribeToSocket(4)
will run. There is no need to get “previous”
userId
because the cleanup function will capture it in a closure.
Other times, you might need to adjust state based on a change in props or other state . This is rarely needed and is usually a sign you have some duplicate or redundant state. However, in the rare case that you need this pattern, you can store previous state or props in state and update them during rendering.
We have previously suggested a custom Hook called
usePrevious
to hold the previous value. However, we’ve found that most use cases fall into the two patterns described above. If your use case is different, you can hold a value in a ref and manually update it when needed. Avoid reading and updating refs during rendering because this makes your component’s behavior difficult to predict and understand.
Any function inside a component, including event handlers and effects, “sees” the props and state from the render it was created in. For example, consider code like this:
function Example() {
const [count, setCount] = useState(0);
function handleAlertClick() {
setTimeout(() => {
alert('You clicked on: ' + count);
}, 3000);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
<button onClick={handleAlertClick}>
Show alert
</button>
</div>
);
}
If you first click “Show alert” and then increment the counter, the alert will show the
count
variable
at the time you clicked the “Show alert” button
. This prevents bugs caused by the code assuming props and state don’t change.
If you intentionally want to read the latest state from some asynchronous callback, you could keep it in a ref, mutate it, and read from it.
Finally, another possible reason you’re seeing stale props or state is if you use the “dependency array” optimization but didn’t correctly specify all the dependencies. For example, if an effect specifies
[]
as the second argument but reads
someProp
inside, it will keep “seeing” the initial value of
someProp
. The solution is to either remove the dependency array, or to fix it. Here’s how you can deal with functions, and here’s other common strategies to run effects less often without incorrectly skipping dependencies.
Note
We provide an
exhaustive-deps
ESLint rule as a part of theeslint-plugin-react-hooks
package. It warns when dependencies are specified incorrectly and suggests a fix.
getDerivedStateFromProps
?
While you probably don’t need it, in rare cases that you do (such as implementing a
<Transition>
component), you can update the state right during rendering. React will re-run the component with updated state immediately after exiting the first render so it wouldn’t be expensive.
Here, we store the previous value of the
row
prop in a state variable so that we can compare:
function ScrollView({row}) {
const [isScrollingDown, setIsScrollingDown] = useState(false);
const [prevRow, setPrevRow] = useState(null);
if (row !== prevRow) {
// Row changed since last render. Update isScrollingDown.
setIsScrollingDown(prevRow !== null && row > prevRow);
setPrevRow(row);
}
return `Scrolling down: ${isScrollingDown}`;
}
This might look strange at first, but an update during rendering is exactly what
getDerivedStateFromProps
has always been like conceptually.
Both
useState
and
useReducer
Hooks bail out of updates if the next value is the same as the previous one. Mutating state in place and calling
setState
will not cause a re-render.
Normally, you shouldn’t mutate local state in React. However, as an escape hatch, you can use an incrementing counter to force a re-render even if the state has not changed:
const [ignored, forceUpdate] = useReducer(x => x + 1, 0);
function handleClick() {
forceUpdate();
}
Try to avoid this pattern if possible.
While you shouldn’t need this often, you may expose some imperative methods to a parent component with the
useImperativeHandle
Hook.
One rudimentary way to measure the position or size of a DOM node is to use a callback ref. React will call that callback whenever the ref gets attached to a different node. Here is a small demo:
function MeasureExample() {
const [height, setHeight] = useState(0);
const measuredRef = useCallback(node => { if (node !== null) { setHeight(node.getBoundingClientRect().height); } }, []);
return (
<>
<h1 ref={measuredRef}>Hello, world</h1> <h2>The above header is {Math.round(height)}px tall</h2>
</>
);
}
We didn’t choose
useRef
in this example because an object ref doesn’t notify us about
changes
to the current ref value. Using a callback ref ensures that even if a child component displays the measured node later (e.g. in response to a click), we still get notified about it in the parent component and can update the measurements.
Note that we pass
[]
as a dependency array to
useCallback
. This ensures that our ref callback doesn’t change between the re-renders, and so React won’t call it unnecessarily.
In this example, the callback ref will be called only when the component mounts and unmounts, since the rendered
<h1>
component stays present throughout any rerenders. If you want to be notified any time a component resizes, you may want to use
ResizeObserver
or a third-party Hook built on it.
If you want, you can extract this logic into a reusable Hook:
function MeasureExample() {
const [rect, ref] = useClientRect(); return (
<>
<h1 ref={ref}>Hello, world</h1>
{rect !== null &&
<h2>The above header is {Math.round(rect.height)}px tall</h2>
}
</>
);
}
function useClientRect() {
const [rect, setRect] = useState(null);
const ref = useCallback(node => {
if (node !== null) {
setRect(node.getBoundingClientRect());
}
}, []);
return [rect, ref];
}
const [thing, setThing] = useState()
mean?
If you’re not familiar with this syntax, check out the explanation in the State Hook documentation.
Yes. See conditionally firing an effect. Note that forgetting to handle updates often introduces bugs, which is why this isn’t the default behavior.
Generally speaking, no.
function Example({ someProp }) {
function doSomething() {
console.log(someProp); }
useEffect(() => {
doSomething();
}, []); // 🔴 This is not safe (it calls `doSomething` which uses `someProp`)}
It’s difficult to remember which props or state are used by functions outside of the effect. This is why usually you’ll want to declare functions needed by an effect inside of it. Then it’s easy to see what values from the component scope that effect depends on:
function Example({ someProp }) {
useEffect(() => {
function doSomething() {
console.log(someProp); }
doSomething();
}, [someProp]); // ✅ OK (our effect only uses `someProp`)}
If after that we still don’t use any values from the component scope, it’s safe to specify
[]
:
useEffect(() => {
function doSomething() {
console.log('hello');
}
doSomething();
}, []); // ✅ OK in this example because we don't use *any* values from component scope
Depending on your use case, there are a few more options described below.
Note
We provide the
exhaustive-deps
ESLint rule as a part of theeslint-plugin-react-hooks
package. It helps you find components that don’t handle updates consistently.
Let’s see why this matters.
If you specify a list of dependencies as the last argument to
useEffect
,
useLayoutEffect
,
useMemo
,
useCallback
, or
useImperativeHandle
, it must include all values that are used inside the callback and participate in the React data flow. That includes props, state, and anything derived from them.
It is only safe to omit a function from the dependency list if nothing in it (or the functions called by it) references props, state, or values derived from them. This example has a bug:
function ProductPage({ productId }) {
const [product, setProduct] = useState(null);
async function fetchProduct() {
const response = await fetch('http://myapi/product/' + productId); // Uses productId prop const json = await response.json();
setProduct(json);
}
useEffect(() => {
fetchProduct();
}, []); // 🔴 Invalid because `fetchProduct` uses `productId` // ...
}
The recommended fix is to move that function inside of your effect . That makes it easy to see which props or state your effect uses, and to ensure they’re all declared:
function ProductPage({ productId }) {
const [product, setProduct] = useState(null);
useEffect(() => {
// By moving this function inside the effect, we can clearly see the values it uses. async function fetchProduct() { const response = await fetch('http://myapi/product/' + productId); const json = await response.json(); setProduct(json); }
fetchProduct();
}, [productId]); // ✅ Valid because our effect only uses productId // ...
}
This also allows you to handle out-of-order responses with a local variable inside the effect:
useEffect(() => {
let ignore = false; async function fetchProduct() {
const response = await fetch('http://myapi/product/' + productId);
const json = await response.json();
if (!ignore) setProduct(json); }
fetchProduct();
return () => { ignore = true }; }, [productId]);
We moved the function inside the effect so it doesn’t need to be in its dependency list.
Tip
Check out this small demo and this article to learn more about data fetching with Hooks.
If for some reason you can’t move a function inside an effect, there are a few more options:
useCallback
Hook. This ensures it doesn’t change on every render unless
its own
dependencies also change:
function ProductPage({ productId }) {
// ✅ Wrap with useCallback to avoid change on every render const fetchProduct = useCallback(() => { // ... Does something with productId ... }, [productId]); // ✅ All useCallback dependencies are specified
return <ProductDetails fetchProduct={fetchProduct} />;
}
function ProductDetails({ fetchProduct }) {
useEffect(() => {
fetchProduct();
}, [fetchProduct]); // ✅ All useEffect dependencies are specified
// ...
}
Note that in the above example we
need
to keep the function in the dependencies list. This ensures that a change in the
productId
prop of
ProductPage
automatically triggers a refetch in the
ProductDetails
component.
Sometimes, your effect may be using state that changes too often. You might be tempted to omit that state from a list of dependencies, but that usually leads to bugs:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1); // This effect depends on the `count` state }, 1000);
return () => clearInterval(id);
}, []); // 🔴 Bug: `count` is not specified as a dependency
return <h1>{count}</h1>;
}
The empty set of dependencies,
[]
, means that the effect will only run once when the component mounts, and not on every re-render. The problem is that inside the
setInterval
callback, the value of
count
does not change, because we’ve created a closure with the value of
count
set to
0
as it was when the effect callback ran. Every second, this callback then calls
setCount(0 + 1)
, so the count never goes above 1.
Specifying
[count]
as a list of dependencies would fix the bug, but would cause the interval to be reset on every change. Effectively, each
setInterval
would get one chance to execute before being cleared (similar to a
setTimeout
.) That may not be desirable. To fix this, we can use the functional update form of
setState
. It lets us specify
how
the state needs to change without referencing the
current
state:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + 1); // ✅ This doesn't depend on `count` variable outside }, 1000);
return () => clearInterval(id);
}, []); // ✅ Our effect doesn't use any variables in the component scope
return <h1>{count}</h1>;
}
(The identity of the
setCount
function is guaranteed to be stable so it’s safe to omit.)
Now, the
setInterval
callback executes once a second, but each time the inner call to
setCount
can use an up-to-date value for
count
(called
c
in the callback here.)
In more complex cases (such as if one state depends on another state), try moving the state update logic outside the effect with the
useReducer
Hook. This article offers an example of how you can do this.
The identity of the
dispatch
function from
useReducer
is always stable
— even if the reducer function is declared inside the component and reads its props.
As a last resort, if you want something like
this
in a class, you can use a ref to hold a mutable variable. Then you can write and read to it. For example:
function Example(props) {
// Keep latest props in a ref. const latestProps = useRef(props); useEffect(() => { latestProps.current = props; });
useEffect(() => {
function tick() {
// Read latest props at any time console.log(latestProps.current); }
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []); // This effect never re-runs}
Only do this if you couldn’t find a better alternative, as relying on mutation makes components less predictable. If there’s a specific pattern that doesn’t translate well, file an issue with a runnable example code and we can try to help.
shouldComponentUpdate
?
You can wrap a function component with
React.memo
to shallowly compare its props:
const Button = React.memo((props) => {
// your component
});
It’s not a Hook because it doesn’t compose like Hooks do.
React.memo
is equivalent to
PureComponent
, but it only compares props. (You can also add a second argument to specify a custom comparison function that takes the old and new props. If it returns true, the update is skipped.)
React.memo
doesn’t compare state because there is no single state object to compare. But you can make children pure too, or even optimize individual children with
useMemo
.
The
useMemo
Hook lets you cache calculations between multiple renders by “remembering” the previous computation:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
This code calls
computeExpensiveValue(a, b)
. But if the dependencies
[a, b]
haven’t changed since the last value,
useMemo
skips calling it a second time and simply reuses the last value it returned.
Remember that the function passed to
useMemo
runs during rendering. Don’t do anything there that you wouldn’t normally do while rendering. For example, side effects belong in
useEffect
, not
useMemo
.
You may rely on
useMemo
as a performance optimization, not as a semantic guarantee.
In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without
useMemo
— and then add it to optimize performance. (For rare cases when a value must
never
be recomputed, you can lazily initialize a ref.)
Conveniently,
useMemo
also lets you skip an expensive re-render of a child:
function Parent({ a, b }) {
// Only re-rendered if `a` changes:
const child1 = useMemo(() => <Child1 a={a} />, [a]);
// Only re-rendered if `b` changes:
const child2 = useMemo(() => <Child2 b={b} />, [b]);
return (
<>
{child1}
{child2}
</>
)
}
Note that this approach won’t work in a loop because Hook calls can’t be placed inside loops. But you can extract a separate component for the list item, and call
useMemo
there.
useMemo
lets you memoize an expensive calculation if the dependencies are the same. However, it only serves as a hint, and doesn’t
guarantee
the computation won’t re-run. But sometimes you need to be sure an object is only created once.
The first common use case is when creating the initial state is expensive:
function Table(props) {
// ⚠️ createRows() is called on every render
const [rows, setRows] = useState(createRows(props.count));
// ...
}
To avoid re-creating the ignored initial state, we can pass a
function
to
useState
:
function Table(props) {
// ✅ createRows() is only called once
const [rows, setRows] = useState(() => createRows(props.count));
// ...
}
React will only call this function during the first render. See the
useState
API reference.
You might also occasionally want to avoid re-creating the
useRef()
initial value.
For example, maybe you want to ensure some imperative class instance only gets created once:
function Image(props) {
// ⚠️ IntersectionObserver is created on every render
const ref = useRef(new IntersectionObserver(onIntersect));
// ...
}
useRef
does not
accept a special function overload like
useState
. Instead, you can write your own function that creates and sets it lazily:
function Image(props) {
const ref = useRef(null);
// ✅ IntersectionObserver is created lazily once
function getObserver() {
if (ref.current === null) {
ref.current = new IntersectionObserver(onIntersect);
}
return ref.current;
}
// When you need it, call getObserver()
// ...
}
This avoids creating an expensive object until it’s truly needed for the first time. If you use Flow or TypeScript, you can also give
getObserver()
a non-nullable type for convenience.
No. In modern browsers, the raw performance of closures compared to classes doesn’t differ significantly except in extreme scenarios.
In addition, consider that the design of Hooks is more efficient in a couple ways:
Traditionally, performance concerns around inline functions in React have been related to how passing new callbacks on each render breaks
shouldComponentUpdate
optimizations in child components. Hooks approach this problem from three sides.
The
useCallback
Hook lets you keep the same callback reference between re-renders so that
shouldComponentUpdate
continues to work:
// Will not change unless `a` or `b` changes
const memoizedCallback = useCallback(() => { doSomething(a, b);
}, [a, b]);
useMemo
Hook makes it easier to control when individual children update, reducing the need for pure components.
useReducer
Hook reduces the need to pass callbacks deeply, as explained below.
We’ve found that most people don’t enjoy manually passing callbacks through every level of a component tree. Even though it is more explicit, it can feel like a lot of “plumbing”.
In large component trees, an alternative we recommend is to pass down a
dispatch
function from
useReducer
via context:
const TodosDispatch = React.createContext(null);
function TodosApp() {
// Note: `dispatch` won't change between re-renders const [todos, dispatch] = useReducer(todosReducer);
return (
<TodosDispatch.Provider value={dispatch}>
<DeepTree todos={todos} />
</TodosDispatch.Provider>
);
}
Any child in the tree inside
TodosApp
can use the
dispatch
function to pass actions up to
TodosApp
:
function DeepChild(props) {
// If we want to perform an action, we can get dispatch from context. const dispatch = useContext(TodosDispatch);
function handleClick() {
dispatch({ type: 'add', text: 'hello' });
}
return (
<button onClick={handleClick}>Add todo</button>
);
}
This is both more convenient from the maintenance perspective (no need to keep forwarding callbacks), and avoids the callback problem altogether. Passing
dispatch
down like this is the recommended pattern for deep updates.
Note that you can still choose whether to pass the application
state
down as props (more explicit) or as context (more convenient for very deep updates). If you use context to pass down the state too, use two different context types — the
dispatch
context never changes, so components that read it don’t need to rerender unless they also need the application state.
useCallback
?
Note
We recommend to pass
dispatch
down in context rather than individual callbacks in props. The approach below is only mentioned here for completeness and as an escape hatch.
In some rare cases you might need to memoize a callback with
useCallback
but the memoization doesn’t work very well because the inner function has to be re-created too often. If the function you’re memoizing is an event handler and isn’t used during rendering, you can use ref as an instance variable, and save the last committed value into it manually:
function Form() {
const [text, updateText] = useState('');
const textRef = useRef();
useEffect(() => {
textRef.current = text; // Write it to the ref });
const handleSubmit = useCallback(() => {
const currentText = textRef.current; // Read it from the ref alert(currentText);
}, [textRef]); // Don't recreate handleSubmit like [text] would do
return (
<>
<input value={text} onChange={e => updateText(e.target.value)} />
<ExpensiveTree onSubmit={handleSubmit} />
</>
);
}
This is a rather convoluted pattern but it shows that you can do this escape hatch optimization if you need it. It’s more bearable if you extract it to a custom Hook:
function Form() {
const [text, updateText] = useState('');
// Will be memoized even if `text` changes:
const handleSubmit = useEventCallback(() => { alert(text);
}, [text]);
return (
<>
<input value={text} onChange={e => updateText(e.target.value)} />
<ExpensiveTree onSubmit={handleSubmit} />
</>
);
}
function useEventCallback(fn, dependencies) { const ref = useRef(() => {
throw new Error('Cannot call an event handler while rendering.');
});
useEffect(() => {
ref.current = fn;
}, [fn, ...dependencies]);
return useCallback(() => {
const fn = ref.current;
return fn();
}, [ref]);
}
In either case, we don’t recommend this pattern and only show it here for completeness. Instead, it is preferable to avoid passing callbacks deep down.
React keeps track of the currently rendering component. Thanks to the Rules of Hooks, we know that Hooks are only called from React components (or custom Hooks — which are also only called from React components).
There is an internal list of “memory cells” associated with each component. They’re just JavaScript objects where we can put some data. When you call a Hook like
useState()
, it reads the current cell (or initializes it during the first render), and then moves the pointer to the next one. This is how multiple
useState()
calls each get independent local state.
Hooks synthesize ideas from several different sources:
adopt
keyword proposal as a sugar syntax for render props.
Sebastian Markbåge came up with the original design for Hooks, later refined by Andrew Clark, Sophie Alpert, Dominic Gannaway, and other members of the React team.