反应挂钩状态不会在递归中改变
React hook state not change in recursion
我有一个递归自定义挂钩,它触发 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>
);
}
您的 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
(我还添加了对竞争条件的预防,因此您可以在单击重置按钮时停止之前的超时。)
您的代码有一个问题。
在您设置新值之前,您检查过它
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>
);
}
我有一个递归自定义挂钩,它触发 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>
);
}
您的 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
(我还添加了对竞争条件的预防,因此您可以在单击重置按钮时停止之前的超时。)
您的代码有一个问题。
在您设置新值之前,您检查过它
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>
);
}