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.

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


📬 The Monthly Upmostly Newsletter

One email a month, packed with the latest React tutorials, delivered straight to your inbox.
Zero spam, just great content. Unsubscribe at any time.
James King headshot
👋 Hey, I'm James King
My tutorials help 60,000+ developers learn React and JavaScript every month. If you'd like to receive a friendly email once in a while of all new React tutorials, just pop your email above! I appreciate the support!

💬 Leave a comment

Your email address will not be published. Required fields are marked *

We will never share your email with anyone else.

Comments

Bern says:

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.