ReactJS在UseEffect内部使用SetInterval导致状态丢失
ReactJS Use SetInterval inside UseEffect Causes State Loss
所以我在 create-react-app 中编写了一个产品原型,在我的 App.js 中,在 app() 函数中,我有:
const [showCanvas, setShowCanvas] = useState(true)
这个状态是由一个带有onClick功能的按钮控制的;然后我有一个函数,在它里面, detectDots 函数应该是 运行 在一个区间内:
const runFaceDots = async (key, dot) => {
const net = await facemesh.load(...);
setInterval(() => {
detectDots(net, key, dot);
}, 10);
// return ()=>clearInterval(interval);};
detectDots 函数的工作原理如下:
const detectDots = async (net, key, dot) => {
...
console.log(showCanvas);
requestFrame(()=>{drawDots(..., showCanvas)});
}
}};
我有一个像这样的 useEffect:
useEffect(()=>{
runFaceDots(); return () => {clearInterval(runFaceDots)}}, [showCanvas])
最后,我可以通过单击这两个按钮来更改状态:
return (
...
<Button
onClick={()=>{setShowCanvas(true)}}>
Show Canvas
</Button>
<Button
onClick={()=> {setShowCanvas(false)}}>
Hide Canvas
</Button>
...
</div>);
网上查了几个帖子,说不清除间隔会导致状态丢失。在我的例子中,我从 useEffect 看到了一些 st运行ge 行为:当我使用 onClick 来设置 ShowCanvas(false) 时,控制台显示 console.log(showCanvas) 不断地从 true 来回切换到 false。
a screenshot of the console message
您最初可以看到,showCanvas 状态为 true,这是有道理的。但是当我点击“隐藏canvas”按钮时,我只点击了一次,showCanvas被设置为false,它应该保持false,因为我没有点击“显示canvas”按钮.
我很困惑,希望有人能帮助。
尝试对 runFaceDots
函数使用 useCallback - https://reactjs.org/docs/hooks-reference.html#usecallback
并确保你 return setInterval 变量来清除计时器。
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])
然后将 useEffect 更改为此 - 运行 仅当 showCanvas 为真时该函数
useEffect(()=>{
if (showCanvas) {
const timer = runFaceDots();
return () => {clearInterval(timer)}
}
}, [showCanvas])
更新:使用全局计时器
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])
.....
}
所以我在 create-react-app 中编写了一个产品原型,在我的 App.js 中,在 app() 函数中,我有:
const [showCanvas, setShowCanvas] = useState(true)
这个状态是由一个带有onClick功能的按钮控制的;然后我有一个函数,在它里面, detectDots 函数应该是 运行 在一个区间内:
const runFaceDots = async (key, dot) => {
const net = await facemesh.load(...);
setInterval(() => {
detectDots(net, key, dot);
}, 10);
// return ()=>clearInterval(interval);};
detectDots 函数的工作原理如下:
const detectDots = async (net, key, dot) => {
...
console.log(showCanvas);
requestFrame(()=>{drawDots(..., showCanvas)});
}
}};
我有一个像这样的 useEffect:
useEffect(()=>{
runFaceDots(); return () => {clearInterval(runFaceDots)}}, [showCanvas])
最后,我可以通过单击这两个按钮来更改状态:
return (
...
<Button
onClick={()=>{setShowCanvas(true)}}>
Show Canvas
</Button>
<Button
onClick={()=> {setShowCanvas(false)}}>
Hide Canvas
</Button>
...
</div>);
网上查了几个帖子,说不清除间隔会导致状态丢失。在我的例子中,我从 useEffect 看到了一些 st运行ge 行为:当我使用 onClick 来设置 ShowCanvas(false) 时,控制台显示 console.log(showCanvas) 不断地从 true 来回切换到 false。
a screenshot of the console message
您最初可以看到,showCanvas 状态为 true,这是有道理的。但是当我点击“隐藏canvas”按钮时,我只点击了一次,showCanvas被设置为false,它应该保持false,因为我没有点击“显示canvas”按钮.
我很困惑,希望有人能帮助。
尝试对 runFaceDots
函数使用 useCallback - https://reactjs.org/docs/hooks-reference.html#usecallback
并确保你 return setInterval 变量来清除计时器。
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])
然后将 useEffect 更改为此 - 运行 仅当 showCanvas 为真时该函数
useEffect(()=>{
if (showCanvas) {
const timer = runFaceDots();
return () => {clearInterval(timer)}
}
}, [showCanvas])
更新:使用全局计时器
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])
.....
}