I have a page which displays some data retrieved every 180 seconds (3 minutes) from an API. When the page loads I have an initial useEffect hook to call the retrieveData() function. Then, using the below code and a state variable called elapsed, I start an interval for the 180 seconds. Every one second, I update a progress bar which is counting down from 3 minutes and displays the remaining time. This uses the elapsed state variable.
This is the hook to start the interval:
useEffect(() => {
const interval = setInterval(() => {
if (elapsed < dataUpdateInterval) {
setElapsed(elapsed + 1);
} else {
setElapsed(0);
retrieveData();
}
}, 1000);
return () => clearInterval(interval);
}, [elapsed]);
My question is, why are the rest of the components on the page being re-rendered on each tick of the interval? I thought that since I'm only updating the elapsed state variable, only those component will update that use that state variable. My table which is displaying the data (stored in another state variable) should only update every 3 minutes.
Happy to provide other info if this is not enough to go off of.
UPDATE 1: In the dev tools, the reason I get for the re-render is "Hooks changed"
UPDATE 2: Below is a snippet of the code where this timer is used. The StatusList and StatusListItem components use React.memo and are functional components. The status list is an IonList and the StatusListItem is an IonItem at top level (from Ionic)
import React, { useEffect, useState } from "react";
import { Layout } from "antd";
import StatusListItemNumbered from "./StatusListItemNumbered";
import { Container, Row, Col } from "react-bootstrap";
import StatusList from "./StatusList";
import { Progress } from "antd";
import moment from "moment";
const Content = React.memo(() => {
const dataUpdateInterval = 180;
const [elapsed, setElapsed] = useState(0);
const [data, setData] = useState();
const timeLeft = (interval, elapsed) => {
let time = moment.duration(interval - elapsed, "seconds");
return moment.utc(time.asMilliseconds()).format("mm:ss");
};
const retrieveData = () => {
console.log("Retrieving data");
SomeApi.getData().then(items => {
setData(items);
})
};
//The effect hook responsible for the timer
useEffect(() => {
setTimeout(() => {
if (elapsed < dataUpdateInterval) {
setElapsed(elapsed + 1);
} else {
setElapsed(0);
retrieveData();
}
}, 1000);
}, [elapsed]);
//Retrieve data the very first time the component loads.
useEffect(() => {
retrieveData();
}, []);
//Component styling
return (
<Layout.Content style={{ padding: "20px 20px" }}>
<div className={css(styles.siteLayoutContent)}>
<Container className={css(styles.mainContainer)}>
<Row className={css(styles.progressRow)}>
<Col>
<Progress
style={{ marginLeft: "17px", width: "99%" }}
strokeColor={{
"0%": "#108ee9",
"100%": "#87d068",
}}
percent={(100 / dataUpdateInterval) * elapsed}
format={() => `${timeLeft(dataUpdateInterval, elapsed)}`}
/>
</Col>
</Row>
<Row>
<Col sm={6}>
<StatusList listName="Data">
{data &&
data.map((item, index) => {
return (
<StatusListItemNumbered
key={index}
value={item.count}
label={item.company}
/>
);
})}
</StatusList>
</Col>
</Row>
</Container>
</div>
</Layout.Content>
);
});
export default Content;