So I am writing a product prototype in create-react-app, and in my App.js, inside the app() function, I have:
const [showCanvas, setShowCanvas] = useState(true)
This state is controlled by a button with an onClick function; And then I have a function, inside it, the detectDots function should be ran in an interval:
const runFaceDots = async (key, dot) => {
const net = await facemesh.load(...);
setInterval(() => {
detectDots(net, key, dot);
}, 10);
// return ()=>clearInterval(interval);};
And the detectDots function works like this:
const detectDots = async (net, key, dot) => {
...
console.log(showCanvas);
requestFrame(()=>{drawDots(..., showCanvas)});
}
}};
I have a useEffect like this:
useEffect(()=>{
runFaceDots(); return () => {clearInterval(runFaceDots)}}, [showCanvas])
And finally, I can change the state by clicking these two buttons:
return (
...
<Button
onClick={()=>{setShowCanvas(true)}}>
Show Canvas
</Button>
<Button
onClick={()=> {setShowCanvas(false)}}>
Hide Canvas
</Button>
...
</div>);
I checked a few posts online, saying that not clearing interval would cause state loss. In my case, I see some strange behaviour from useEffect: when I use onClick to setShowCanvas(false), the console shows that console.log(showCanvas) keeps switching from true to false back and forth.
a screenshot of the console message
you can see initially, the showCanvas state was true, which makes sense. But when I clicked the "hide canvas" button, and I only clicked it once, the showCanvas was set to false, and it should stay false, because I did not click the "show canvas" button.
I am very confused and hope someone could help.
Try using useCallback for runFaceDots
function - https://reactjs.org/docs/hooks-reference.html#usecallback
And ensure you return the setInterval variable to clear the timer.
const runFaceDots = useCallback(async (key, dot) => {
const net = await facemesh.load(...);
const timer = setInterval(() => {
detectDots(net, key, dot);
}, 10);
return timer //this is to be used for clearing the interval
},[showCanvas])
Then change useEffect to this - running the function only if showCanvas is true
useEffect(()=>{
if (showCanvas) {
const timer = runFaceDots();
return () => {clearInterval(timer)}
}
}, [showCanvas])
Update: Using a global timer
let timer // <-- create the variable outside the component.
const MyComponent = () => {
.....
useEffect(()=>{
if (showCanvas) {
runFaceDots(); // You can remove const timer here
return () => {clearInterval(timer)}
} else {
clearInterval(timer) //<-- clear the interval when hiding
}
}, [showCanvas])
const runFaceDots = useCallback(async (key, dot) => {
const net = await facemesh.load(...);
timer = setInterval(() => { //<--- remove const and use global variable
detectDots(net, key, dot);
}, 10);
return timer //this is to be used for clearing the interval
},[showCanvas])
.....
}