Javascript useState中的变量未在useEffect回调中更新
我在使用useState和useEffect挂钩时遇到问题Javascript useState中的变量未在useEffect回调中更新,javascript,reactjs,react-hooks,use-effect,Javascript,Reactjs,React Hooks,Use Effect,我在使用useState和useEffect挂钩时遇到问题 import { useState, useEffect } from "react"; const counter = ({ count, speed }) => { const [inc, setInc] = useState(0); useEffect(() => { const counterInterval = setInterval(() => {
import { useState, useEffect } from "react";
const counter = ({ count, speed }) => {
const [inc, setInc] = useState(0);
useEffect(() => {
const counterInterval = setInterval(() => {
if(inc < count){
setInc(inc + 1);
}else{
clearInterval(counterInterval);
}
}, speed);
}, [count]);
return inc;
}
export default counter;
从“react”导入{useState,useffect};
常量计数器=({count,speed})=>{
const[inc,setInc]=useState(0);
useffect(()=>{
常量计数器间隔=设置间隔(()=>{
如果(包括<计数){
setInc(inc+1);
}否则{
净间隔(反间隔);
}
},速度);
},[计数];
返回公司;
}
导出默认计数器;
上面的代码是一个计数器组件,它在props中接受count,然后用0初始化inc并递增,直到它等于count
问题是每次我得到0时,我没有在useEffect和setInterval的回调中得到inc的更新值,因此它将inc呈现为1,而setInterval永远无法清除。我认为inc必须关闭useffect和setInterval的回调,所以我必须在那里获得更新inc,所以可能是一个bug
我不能在依赖项中传递inc(这在其他类似问题中建议),因为在我的例子中,我已经在useffect中设置了interval,所以在依赖项数组中传递inc会导致无限循环
我有一个使用有状态组件的工作解决方案,但我想使用功能组件来实现这一点。这里的问题是每次运行
useffect
时都会定义count
更新时的clearInterval
回调。定义时,inc
的值是将在回调中读取的值
此编辑有不同的方法。我们包含一个ref,用于跟踪inc
小于count
,如果小于,我们可以继续递增inc
。如果不是,那么我们清除计数器(正如您在问题中所做的)。每次inc
更新时,我们都会评估它是否仍然小于count,并将其保存在ref
中。然后在前面的useffect
中使用此值
正如@DennisVash在他的回答中正确指出的那样,我包括了对速度的依赖
const useCounter = ({ count, speed }) => {
const [inc, setInc] = useState(0);
const inc_lt_count = useRef(inc < count);
useEffect(() => {
const counterInterval = setInterval(() => {
if (inc_lt_count.current) {
setInc(inc => inc + 1);
} else {
clearInterval(counterInterval);
}
}, speed);
return () => clearInterval(counterInterval);
}, [count, speed]);
useEffect(() => {
if (inc < count) {
inc_lt_count.current = true;
} else {
inc_lt_count.current = false;
}
}, [inc, count]);
return inc;
};
const useCounter=({count,speed})=>{
const[inc,setInc]=useState(0);
const inc_lt_count=useRef(inc{
常量计数器间隔=设置间隔(()=>{
如果(包括当前计数){
setInc(inc=>inc+1);
}否则{
净间隔(反间隔);
}
},速度);
return()=>clearInterval(计数器间隔);
},[计数,速度];
useffect(()=>{
如果(包括<计数){
inc_lt_count.current=true;
}否则{
inc_lt_count.current=false;
}
},[inc,count]);
返回公司;
};
需要处理的主要问题是根据道具情况确定的清理间隔
您应该在功能设置状态中添加条件检查:
setInc(inc => (inc < count ? inc + 1 : inc));
有几个问题:
useffect
返回函数来清除间隔inc
值不同步,因为您没有使用以前的inc
值const counter = ({ count, speed }) => {
const [inc, setInc] = useState(0);
useEffect(() => {
const counterInterval = setInterval(() => {
setInc(inc => {
if(inc < count){
return inc + 1;
}else{
// Make sure to clear the interval in the else case, or
// it will keep running (even though you don't see it)
clearInterval(counterInterval);
return inc;
}
});
}, speed);
// Clear the interval every time `useEffect` runs
return () => clearInterval(counterInterval);
}, [count, speed]);
return inc;
}
还有第三种更简单的方法:
在deps数组中包括inc
,如果inc>=count
,则在调用setInterval
之前提前返回:
const [inc, setInc] = useState(0);
useEffect(() => {
if (inc >= count) return;
const counterInterval = setInterval(() => {
setInc(inc + 1);
}, speed);
return () => clearInterval(counterInterval);
}, [count, speed, inc]);
return inc;
对否决票有什么解释吗?尝试使用回调使用最新的值:
setInc(inc=>inc+1)代码>。如果有帮助,请告诉我。请告诉我们如何使用此挂钩,然后请注意,挂钩不能像useEffect需要speed
和inc
作为依赖项那样简单,您应该返回一个函数来清除interval@Alvaro,我尝试使用setInc(inc=>inc+1);现在我在setInc的回调中得到更新的inc,但在setInterval的回调中仍然没有得到更新的inc,所以它永远不会进入else条件,从而导致无穷大loop@DennisVash谢谢你让我知道,你能详细说明为什么它不起作用吗?你检查过了吗?原因有很多,主要是增加引用不会导致重新渲染,在父组件(我使用计数器的地方)中,count的值来自服务器,这就是为什么我将其添加到依赖项数组中,我可以通过在API调用后装载计数器组件来移除其依赖项。因此,useEffect将作为componentDidMount工作。现在,使用useRef不会重新渲染组件。所以我需要状态来重新加载ComponentNC。当前值正在获取更新,但react不会重新加载它,因为它不在state@AbhaySehgal这就是我所说的“副作用”。因此,您需要在一个州内使用inc
。好的,让我来编辑。这可能适用于这个特定的用例,但如果组件太频繁地重新呈现,会导致错误:您能重现任何错误吗?请尝试setInc函数中的console.log(),它调用infinitelyIt将一直运行到卸载,我想您想清除interval on条件,让我来修复it@AbhaySehgal我编辑了答案,现在它在计数条件下清除选项一:否则情况是返回inc,然后清除setInterval,这样该行将永远不会执行,并将导致无限循环所有选项都工作正常,感谢您的回答,如果应该返回setInc(inc+1),第二个选项中有一个问题
const counter = ({ count, speed }) => {
const [inc, setInc] = useState(0);
useEffect(() => {
const counterInterval = setInterval(() => {
if(inc < count){
return setInc(inc + 1);
}else{
// Make sure to clear your interval in the else case,
// or it will keep running (even though you don't see it)
clearInterval(counterInterval);
}
}, speed);
// Clear the interval every time `useEffect` runs
return () => clearInterval(counterInterval);
}, [count, speed, inc]);
return inc;
}
const [inc, setInc] = useState(0);
useEffect(() => {
if (inc >= count) return;
const counterInterval = setInterval(() => {
setInc(inc + 1);
}, speed);
return () => clearInterval(counterInterval);
}, [count, speed, inc]);
return inc;