A timer representing setTimeout inside of a React component

Use setTimeout in your React components to execute a function or block of code after a period of time. Let’s explore how to use setTimeout in React. There is also a similar method called setInterval, you can learn more about it from this guide

The TL;DR:

useEffect(() => { const timer = setTimeout(() => { console.log('This will run after 1 second!') }, 1000); return () => clearTimeout(timer); }, []);

What is setTimeout?

The setTimeout method calls a function or runs some code after a period of time, specified using the second argument.

For example, the code below prints “Hello, World!” to the developer console after 3,000 milliseconds (or 3 seconds).

setTimeout(() => { console.log('Hello, World!') }, 3000);

Using setTimeout in React Components

Using setTimeout inside of a React component is easy enough as it’s just a regular JavaScript method.

For instance, let’s use setTimeout inside of a functional React component which uses Hooks. We’ll call setTimeout inside of the useEffect Hook, which is the equivalent of the componentDidMount lifecycle method in Class components.

App.js
import React, { useEffect } from 'react'; const App = () => { useEffect(() => { const timer = setTimeout(() => { setCount('Timeout called!'); }, 1000); return () => clearTimeout(timer); }, []); return ( <div> Hello, World </div> ); }; export default App;

Our functional component runs the useEffect method when it first renders. If you want to learn more about Hooks, I recommend my tutorial on Simplifying Forms using React Hooks.

We schedule a new setTimeout called timer when the App component mounts for the first time.

As a result, the code inside of the setTimeout block runs after 1 second as indicated by the 1000 millisecond value that’s passed into the second parameter of the setTimeout method.

Clearing setTimeout

A setTimeout timer must be cleared and handle properly, otherwise, you may experience adverse side effects in your code.

To clear or cancel a timer, you call the clearTimeout(); method, passing in the timer object that you created into clearTimeout().

For example, the code below shows how to properly clear a timer inside of a functional React component.

App.js
... const App = () => { useEffect(() => { const timer = setTimeout(() => console.log("Hello, World!"), 3000); return () => clearTimeout(timer); }, []); }; ...

And an example of clearing setTimeout inside of a React Class component:

App.js
... class App extends Component { let timer = null; componentDidMount() { timer = setTimeout(() => console.log('Hello, World!'), 3000) } componentWillUnmount() { clearTimeout(timer); } } ...

Above all, it’s important that you clear timers, otherwise you could experience errors in your code.

You’ll notice that in the first example of clearing a timer inside of a functional component, we’re returning an anonymous function from the useEffect Hook. This is the equivalent of componentWillUnmount as shown in the second example.

setTimeout Gotchas

Using a state property inside of a setTimeout does not use the current value of that state property.

I found this odd issue with setTimeout and state when I was trying to access a state prop inside of setTimeout.

Take the example below:

TimeoutExample.js
import React, { useEffect, useState } from 'react'; const TimeoutExample = () => { const [count, setCount] = useState(0); const [countInTimeout, setCountInTimeout] = useState(0); useEffect(() => { setTimeout(() => { setCountInTimeout(count); // count is 0 here }, 3000); setCount(5); // Update count to be 5 after timeout is scheduled }, []); return ( <div> Count: {count} <br /> setTimeout Count: {countInTimeout} </div> ); }; export default TimeoutExample;

You would expect the value of countInTimeout to be 5, but it’s actually 0.

Huh?!

setTimeout is a closure, therefore, when setTimeout is scheduled it uses the value of count at that exact moment in time, which is the initial value of 0.

To solve this, use the useRef Hook:

function App() { const [count, setCount] = useState(0); const countRef = useRef(count); countRef.current = count; const getCountTimeout = () => { setTimeout(() => { setTimeoutCount(countRef.current); }, 2000); }; return ( ... ) }

This solution is thanks to a discussion on Github: https://github.com/facebook/react/issues/14010

👋 Hey, I'm James Dietrich
I work full-time at an AI-based startup out of San Francisco, CA. My true passion is to help others. My tutorials help 150,000+ developers learn React and JavaScript every month. Follow on Twitter, or Github.

💬 Leave a comment

Your email address will not be published.

We will never share your email with anyone else.

Comments

Another approach would be to have the effect run when the count value changes by setting count as a dependency of useEffect:

“`
useEffect(() => {
const timer = setTimeout(() => {
setCountInTimeout(count); // count is 0 here
}, 3000);
setCount(5); // Update count to be 5 after timeout is scheduled
return () => clearTimeout(timer)
}, [count]);
“`

The disadvantage of this approach is that useEffect will first set a timer with count set to zero, then setting the new value of count by calling `setCount(5)` will cause useEffect to be called again after the next render, which will then clear the old timer and set a new timer with the count value of 5.

Thanks mate. nice & clear explanation 🙂
I would add that you need to udpate that reference each time the state updates, so both values are synced 😉