Have you ever noticed React setInterval functions acting strangely? I’ve found that React Hooks can often help to fix setInterval
problems.
In this article, I'll demonstrate how to use setInterval
with React Hooks. However, it should be noted that the way setInterval
works in Vanilla JavaScript is generally different.
Overview of SetInterval
At each specified timing event, the setInterval()
React method repeats a block of code.
This is JavaScript's standard setInterval
syntax:
setInterval(function, milliseconds);
Its qualities are:
- Function: The functions store executable code in a local scope.
- Milliseconds: The milliseconds are a timer that triggers the function to execute a line of code.
We won't go into more detail in this React setInterval
introduction. Instead, please refer to these docs.
Understanding the Effects of SetInterval on React Component Rendering
Every time the state of a component changes, React prepares a render. This takes place once a render has been scheduled. React will look for the ideal time to do this. When we call the setState
function in React, we are change the state, which results in an update (in React Hooks, we would use useState
).
A component may re-render itself for one of four reasons: state changes, parent (or child) re-renderings, context changes, and hook changes. A widespread misconception is that when the component's props change, re-renders likewise take place. On its own, this is untrue.
If React executes a component more than once when using setInterval
without a React Hook, the SetInterval
will crash. If you use React and don't use the React Hook to build a counter increment mechanism with React setInterval
in an initial page load, it will crash the counter. Therefore, we'll construct a counter increment in this tutorial using React Hooks.
Bad Practices When Using SetInterval in React.js
Below is an example of the problem with utilizing setInterval
in JavaScript without a React Hook:
Bad Practice
import { useState } from "react";
import { Fragment } from "react";
function App() {
let [count, setCount] = useState(0);
setInterval(() => {
setCount(count + 1);
}, 2000);
return (
<Fragment>
<h1>Count: {count}</h1>
</Fragment>
);
}
export default App;
We can see that setInterval
was not used in a React Hook in the code above, which is not best practice.
The code will be printed when the page loads for the first time, and along the way, the counter will begin to act improperly, which is not the best scenario.
Resolving Conflicts in React.js
Next, we’ll be using setInterval
with React Hooks in various ways.
In this section, we’ll be working with code samples and using a counter in React and a React Hook.
Using SetInterval in a Function-Based Component
Immediately after the page loads for the first time, we'll call setInterval
.
Here, we'll make use of setInterval
by automating its execution upon a page's initial load:
import { useEffect } from "react";
import { useState } from "react";
import { Fragment } from "react";
function App() {
let [count, setCount] = useState({
num: 0,
});
useEffect(() => {
setInterval(() => {
setCount((prevState) => {
return {
num: prevState.num + 1,
};
});
}, 1000);
}, []);
return (
<Fragment>
<div style={{ textAlign: "center" }}>
<h1>Count: {count.num}</h1>
</div>
</Fragment>
);
}
export default App;
React Hooks like useState and useEffect were utilized in the code above.
The useState
is dependent on its prior state:
setCount((prevState) => {
return {
num: prevState.num + 1,
};
});
useEffect(() => {
setInterval(() => {
setCount((prevState) => {
return {
num: prevState.num + 1,
};
});
}, 1000);
}, []);
Output:
As you can see, the useEffect
method, which has a return function, is used in the code above.
The cleaning function (after the user exits the page and the component unmounts) is the return function.
When the app component loads for the first time, the useEffect
hook wraps the setState
counter to execute once. This stops React from entering an endless loop.
Kindly note that you can run the code without the cleanup return function. However, it's best practice to use it.
Using setInterval in a React Class-based Component
Next, we’ll look at JavaScript setInterval
in a class-based component in code below.
Furthermore, to start setInterval
and stop crashes and errors, the setInterval
is inserted inside the ComponentDidMount component.
Read more about the React Lifecycle.
Keep in mind that the interval begins as soon as a component loads for the first time. UseEffect
is utilized in the function base component.
import React from "react";
class App extends React.Component {
state = { count: 0 };
componentDidMount() {
setInterval(() => {
this.setState((prevState) => {
return {
...prevState,
count: prevState.count + 1,
};
});
}, 1000);
}
render() {
return (
<div>
<h1>{this.state.count} Seconds</h1>
</div>
);
}
}
export default App;
Output:
Calling React SetInterval from onClick in a Function-based Component
We can quickly use an onClick
to call setInterval
.
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
const startCountHandler = () => {
setInterval(() => {
setCount((count) => count + 1);
}, 1000);
};
return (
<div style={{ textAlign: "center" }}>
<h1>{count}</h1>
<br />
<button onClick={startCountHandler}>Start</button>
</div>
);
}
export default App;
Output:
Using Clear Interval from an onClick in a Function-based Component
To stop the counter in this area, we'll use clearinterval. It is incredibly simple to use.
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
const [intervalId, setIntervalId] = useState(0);
const startCountHandler = () => {
let newIntervalId = setInterval(() => {
setCount((count) => count + 1);
}, 1000);
setIntervalId(newIntervalId);
};
// Stopping the interval
const stopCountHandler = () => {
clearInterval(intervalId);
};
return (
<div style={{ textAlign: "center" }}>
<h1>{count}</h1>
<br />
<button onClick={startCountHandler}>Start</button>
<br />
<button onClick={stopCountHandler}>Stop</button>
</div>
);
}
export default App;
To store the interval id obtained from setInterval
, a new state was made. The function for establishing the interval is activated when we click the start button, and we added setIntervalId(newIntervalId)
inside the React SetInterval
function to save the interval id in the state we generated for the intervalID
.
Output:
We can start the counter by pressing the start button, and stop the counter by pressing the stop button. By hitting the start button once more, a paused counter can be started again.
Starting SetInterval in a Class-based Component from onClick
In this part, we'll call setInterval
from a class-based component's onClick event.
Note: Take care to bind the ‘this’ keyword, or else you can’t access the this.setState
in the startCountHandler
function.
import React from "react";
class App extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0, intervalId: 0 };
// Binfing this keyword
this.startCountHandler = this.startCountHandler.bind(this);
}
startCountHandler() {
setInterval(() => {
this.setState((prevState) => {
return {
...prevState,
count: prevState.count + 1,
};
});
}, 1000);
}
render() {
return (
<div>
<h1>{this.state.count} Seconds</h1>
<button onClick={this.startCountHandler}>Start</button>
</div>
);
}
}
export default App;
Output:
Stopping the Countdown in Class-based Components Using clearInterval
In this section, we'll utilize clearInterval
in a class-based component to stop the counter from running during an onClick
event.
Let's get going.
The function that will stop or pause the counter increment will be the React setInterval
handler, to which we will bind the keyword "this." Learn more about the “this” keyword.
The code for a class-based component is shown below.
import React, { useEffect, useState } from "react";
class App extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0, intervalId: 0 };
// Binding this keyword
this.startCountHandler = this.startCountHandler.bind(this);
this.stopCountHandler = this.stopCountHandler.bind(this);
}
startCountHandler() {
let newIntervalId = setInterval(() => {
this.setState((prevState) => {
return {
...prevState,
count: prevState.count + 1,
};
});
}, 1000);
// Append the interval Id to state interval
this.setState((prevState) => {
return {
...prevState,
intervalId: newIntervalId,
};
});
}
// Stopping the setInterval with clearinterval
stopCountHandler() {
if (this.state.intervalId) {
clearInterval(this.state.intervalId);
}
}
render() {
return (
<div style={{ textAlign: "center" }}>
<h1>{this.state.count} Seconds</h1>
<button onClick={this.startCountHandler}>Start</button>
<br />
<button onClick={this.stopCountHandler}>Stop</button>
</div>
);
}
}
export default App;
Output:
Utilizing a Backward Counter
import { useState, useEffect } from 'react';
const ForwardCounter = () => {
const [counter, setCounter] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCounter((prevCounter) => prevCounter - 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <h1>{counter}</h1>;
};
export default ForwardCounter;
The aforementioned code is a straightforward example of utilizing setInterval
in a React Hook to interact with a backward counter.
Please be aware that using setInterval
in a React Hook useEffect
will cause the cleanup function to run.
The Cleanup Function in useEffect
In this section, we'll demonstrate how to send a request to a fake server utilizing the useEffect cleanup function rather than while the user is still typing.
The cleaning function, which runs after the user exits the page and the component unmounts, is the return function.
This greatly helps to avoid sending many requests while a user is typing, which would slow down our web app. Creating a search mechanism that returns a list of values from a server in this way is great practice.
Here, we'll implement this process using setTimeout rather than setInterval
.
Now, we’ll use clearInterval to do the cleanup operation.
import { useEffect, useState } from "react";
function App() {
const [enteredValue, setEnteredValue] = useState("");
useEffect(() => {
const interval = setTimeout(() => {
if (enteredValue.length > 0) {
// Make a fetch request in a real project
console.log("Send a request to a server...");
}
}, 1000);
return () => clearInterval(interval);
}, [enteredValue]);
const onChangedHandler = (e) => {
setEnteredValue(e.target.value);
};
return (
<div style={{ textAlign: "center" }}>
<input
type={"text"}
onChange={onChangedHandler}
value={enteredValue}
style={{ padding: "10px", margin: "40px" }}
/>
</div>
);
}
export default App;
Output:
We can see from the output in the console that the console log was produced four times, which indicates that I actually waited four times before inputting another character.
Make sure to test this out for yourself to see how it performs.
All I ask is that you start using this in your React project so that you discover its value.
Conclusion
I hope you had a good time reading this article! We learned how to utilize React setInterval and clearInterval in class & function-based components without crashes and errors, as well as how React renders a component and affects setInterval
. Start making changes to your app so that it takes advantage of the strategies we discussed in this post. Setting up setInterval
in your program allows you to add more features and ensures that everything runs smoothly.