我有一个递归自定义钩子,它为 3 chances
触发 setTimeout 函数,如果 chances
达到 0,它应该停止递归。
但是当代码运行时,setTimeout
中的机会仍然是 = 3,并且递归根本不会停止。
我认为这与关闭有关,但我无法弄清楚它是如何解决这个问题的。请给我解释一下。
示例 https://codesandbox.io/s/recursion-state-closures-30wuo?file=/src/App.js
import { useEffect, useState } from "react";
const DEFAULT_CHANCES = 3;
function useRecursion() {
var [chances, setChances] = useState(DEFAULT_CHANCES);
const resetChances = () => setChances(DEFAULT_CHANCES);
const minusChancesRecursivly = () => {
setTimeout(() => {
setChances((prev) => --prev);
if (chances > 0) {
console.log(chances);
minusChancesRecursivly();
}
}, 1000);
};
useEffect(() => {
minusChancesRecursivly();
return () => setChances(DEFAULT_CHANCES);
}, []);
return [chances, resetChances];
}
export default function App() {
const [chances, resetChances] = useRecursion();
return (
<div className="App">
<button onClick={resetChances}>Click</button>
<h1>Chances: {chances}</h1>
</div>
);
}
答案 0 :(得分:0)
您的 minusChancesRecursivly
出现问题,您在 settimeout 中访问 chances
。每次调用函数时,chances
的值都将与调用时的值保持不变。您可以通过将代码放入要传递给 setChances
的回调中来解决此问题,它接收 prev
参数中机会的当前值。
但更好的方法是将当前机会值作为参数传递给函数,就像这样
const minusChancesRecursivly = (chancesLeft) => {
setChances(chancesLeft);
if (chancesLeft > 0) {
setTimeout(
() => minusChancesRecursivly(chancesLeft - 1),
1000
);
}
};
然后按如下方式调用它
minusChancesRecursivly(3);
在我看来,这会产生更清晰的代码,这些代码独立于技术以相同的方式编写。我的意思是 - 相同的概念在其他框架或语言中的作用相同。
修改后的代码在这里 https://codesandbox.io/s/recursion-state-closures-forked-jbfgo?file=/src/App.js
(我还为竞争条件添加了预防措施,因此您可以在单击重置按钮时停止之前的超时。)
答案 1 :(得分:0)
您的代码有一个问题。
在你设置新值之前你检查过它
setChances((prev) => --prev);
if (chances > 0) {
console.log(chances);
minusChancesRecursivly();
}
这必须有效
import { useEffect, useState } from "react";
const DEFAULT_CHANCES = 3;
function useRecursion() {
var [chances, setChances] = useState(DEFAULT_CHANCES);
const resetChances = () => setChances(DEFAULT_CHANCES);
const minusChancesRecursivly = () => {
setTimeout(() => {
setChances((prev) => --prev);
}, 1000);
};
useEffect(() => {
if (chances > 0) {
minusChancesRecursivly();
}
}, [chances]);
return [chances, resetChances];
}
export default function App() {
const [chances, resetChances] = useRecursion();
return (
<div className="App">
<button onClick={resetChances}>Click</button>
<h1>Chances: {chances}</h1>
</div>
);
}