useState 中的变量未在 useEffect 回调中更新
variable in useState not updating in useEffect callback
我在使用 useState 和 useEffect 挂钩时遇到问题
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;
上面的代码是一个计数器组件,它把count传入props,然后用0初始化inc并自增直到等于count
问题是每次我得到 0 时,我都没有在 useEffect 和 setInterval 的回调中得到 inc 的更新值,所以它将 inc 呈现为 1 并且 setInterval 永远不会变得清晰。我认为 inc 必须关闭使用 useEffect 和 setInterval 的回调,所以我必须在那里获取更新 inc,所以这可能是一个错误?
我不能在依赖项中传递 inc(在其他类似问题中建议),因为在我的例子中,我在 useEffect 中设置了 setInterval,因此在依赖项数组中传递 inc 会导致无限循环
我有一个使用有状态组件的工作解决方案,但我想使用功能组件来实现这个
这里的问题是每次 useEffect
运行时都会定义来自 clearInterval
的回调,也就是 count
更新时。定义时的值 inc
是将在回调中读取的值。
这次编辑采用了不同的方法。我们包含一个 ref 来跟踪 inc
是否小于 count
,如果小于,我们可以继续递增 inc
。如果不是,那么我们会清除计数器(就像您在问题中所做的那样)。每次inc
更新,我们评估它是否仍然小于count并保存在ref
中。然后在前面的useEffect
.
中使用这个值
正如@DennisVash 在他的回答中正确指出的那样,我包含了对 speed
的依赖。
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;
};
主要需要解决的问题是Closures和依赖道具的条件清关间隔
您应该在功能 setState
:
中添加条件检查
setInc(inc => (inc < count ? inc + 1 : inc));
此外,清除间隔应该发生在卸载时。
如果要在条件(inc < count
)上添加clearInterval
,需要保存间隔id和增加的数字的引用:
import React, { useState, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
const useCounter = ({ count, speed }) => {
const [inc, setInc] = useState(0);
const incRef = useRef(inc);
const idRef = useRef();
useEffect(() => {
idRef.current = setInterval(() => {
setInc(inc => (inc < count ? inc + 1 : inc));
incRef.current++;
}, speed);
return () => clearInterval(idRef.current);
}, [count, speed]);
useEffect(() => {
if (incRef.current > count) {
clearInterval(idRef.current);
}
}, [count]);
useEffect(() => {
console.log(incRef.current);
});
return inc;
};
const App = () => {
const inc = useCounter({ count: 10, speed: 1000 });
return <h1>Counter : {inc}</h1>;
};
ReactDOM.render(<App />, document.getElementById('root'));
有几个问题:
- 您没有return使用
useEffect
中的函数来清除间隔
- 您的
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
,这使事情变得更简单,因为您不需要在 setInc
:[=22= 中使用之前的 inc
]
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;
}
还有第三种方法更简单:
在 deps 数组中包含 inc
,如果 inc >= count
、return 在调用 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;
我在使用 useState 和 useEffect 挂钩时遇到问题
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;
上面的代码是一个计数器组件,它把count传入props,然后用0初始化inc并自增直到等于count
问题是每次我得到 0 时,我都没有在 useEffect 和 setInterval 的回调中得到 inc 的更新值,所以它将 inc 呈现为 1 并且 setInterval 永远不会变得清晰。我认为 inc 必须关闭使用 useEffect 和 setInterval 的回调,所以我必须在那里获取更新 inc,所以这可能是一个错误?
我不能在依赖项中传递 inc(在其他类似问题中建议),因为在我的例子中,我在 useEffect 中设置了 setInterval,因此在依赖项数组中传递 inc 会导致无限循环
我有一个使用有状态组件的工作解决方案,但我想使用功能组件来实现这个
这里的问题是每次 useEffect
运行时都会定义来自 clearInterval
的回调,也就是 count
更新时。定义时的值 inc
是将在回调中读取的值。
这次编辑采用了不同的方法。我们包含一个 ref 来跟踪 inc
是否小于 count
,如果小于,我们可以继续递增 inc
。如果不是,那么我们会清除计数器(就像您在问题中所做的那样)。每次inc
更新,我们评估它是否仍然小于count并保存在ref
中。然后在前面的useEffect
.
正如@DennisVash 在他的回答中正确指出的那样,我包含了对 speed
的依赖。
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;
};
主要需要解决的问题是Closures和依赖道具的条件清关间隔
您应该在功能 setState
:
setInc(inc => (inc < count ? inc + 1 : inc));
此外,清除间隔应该发生在卸载时。
如果要在条件(inc < count
)上添加clearInterval
,需要保存间隔id和增加的数字的引用:
import React, { useState, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
const useCounter = ({ count, speed }) => {
const [inc, setInc] = useState(0);
const incRef = useRef(inc);
const idRef = useRef();
useEffect(() => {
idRef.current = setInterval(() => {
setInc(inc => (inc < count ? inc + 1 : inc));
incRef.current++;
}, speed);
return () => clearInterval(idRef.current);
}, [count, speed]);
useEffect(() => {
if (incRef.current > count) {
clearInterval(idRef.current);
}
}, [count]);
useEffect(() => {
console.log(incRef.current);
});
return inc;
};
const App = () => {
const inc = useCounter({ count: 10, speed: 1000 });
return <h1>Counter : {inc}</h1>;
};
ReactDOM.render(<App />, document.getElementById('root'));
有几个问题:
- 您没有return使用
useEffect
中的函数来清除间隔 - 您的
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
,这使事情变得更简单,因为您不需要在 setInc
:[=22= 中使用之前的 inc
]
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;
}
还有第三种方法更简单:
在 deps 数组中包含 inc
,如果 inc >= count
、return 在调用 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;