Learn More

Try out the world’s first micro-repo!

Learn More

Resolving SetInterval Conflicts in React.js

Resolving SetInterval Conflicts in React.js

Have you ever noticed setInterval functions in React 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() 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 setInterval introduction. Instead, please refer to these docs.

Understanding the Effects of SetInterval on the Rendering of Components in React

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 changing 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 setInterval in an initial page load, it will crash the counter. Therefore, we'll construct a counter increment in this tutorial using React Hooks.

The Drawbacks of SetInterval in React.js

Below is an example of the problem with utilizing setInterval 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 the Conflict 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 the useEffect cleanup function.

Using setInterval in a React Class-based Component

Next, we’ll look at 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 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.

Code:

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 SetInterval from onClick in a Function-based Component

We can quickly use an onClick to call setInterval.

Code:

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.

Code:

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 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 Clear Interval

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 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.

Code:

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.

Code:

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 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.

Interested in becoming a Pieces Content Partner?

Learn More

Get our latest blog posts and product updates by signing up for our monthly newsletter! 

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Table of Contents

React

More from Pieces