I've got a hooks problem I've been unable to find an answer for. The closest article being this other post.
Essentially I want to have a function that is invoked once per component lifecycle with hooks. I'd normally use useEffect
as such:
useEffect(() => {
doSomethingOnce()
setInterval(doSomethingOften, 1000)
}, [])
I'm trying not to disable eslint rules and get a react-hooks/exhaustive-deps
warning.
Changing this code to the following creates an issue of my functions being invoked every time the component is rendered... I don't want this.
useEffect(() => {
doSomethingOnce()
setInterval(doSomethingOften, 1000)
}, [doSomethingOnce])
The suggested solution is to wrap my functions in useCallback
but what I can't find the answer to is what if doSomethingOnce
depends on the state.
Below is the minimal code example of what I am trying to achieve:
import "./styles.css";
import { useEffect, useState, useCallback } from "react";
export default function App() {
const [count, setCount] = useState(0);
const getRandomNumber = useCallback(
() => Math.floor(Math.random() * 1000),
[]
);
const startCounterWithARandomNumber = useCallback(() => {
setCount(getRandomNumber());
}, [setCount, getRandomNumber]);
const incrementCounter = useCallback(() => {
setCount(count + 1);
}, [count, setCount]);
useEffect(() => {
startCounterWithARandomNumber();
setInterval(incrementCounter, 1000);
}, [startCounterWithARandomNumber, incrementCounter]);
return (
<div className="App">
<h1>Counter {count}</h1>
</div>
);
}
As you can see from this demo since incrementCounter
depends on count
it gets recreated. Which in turn re-invokes my useEffect
callback that I only wanted to be called the once. The result is that the startCounterWithARandomNumber
and incrementCounter
get called many more times than I would expect.
Any help would be much appreciated.
Update
I should have pointed out, this is a minimal example of a real-use case where the events are aysncronous.
In my real code the context is a live transcription app. I initially make a fetch call to GET /api/all.json
to get the entire transcription then poll GET /api/latest.json
every second merging the very latest speech to text from with the current state. I tried to emulate this in my minimal example with a seed to start followed by a polled method call that has a dependency on the current state.