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.
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.
...
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:
...
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:
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
💬 Leave a comment