Javascript 在setInterval内使用React状态挂钩时状态未更新
我正在试用新的,有一个时钟组件和一个计数器,应该每秒钟增加一次。但是,该值不会增加超过1Javascript 在setInterval内使用React状态挂钩时状态未更新,javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,我正在试用新的,有一个时钟组件和一个计数器,应该每秒钟增加一次。但是,该值不会增加超过1 功能时钟(){ const[time,setTime]=React.useState(0); React.useffect(()=>{ const timer=window.setInterval(()=>{ 设置时间(时间+1); }, 1000); return()=>{ 窗口清除间隔(计时器); }; }, []); 返回( 秒:{time} ); } ReactDOM.render(,docume
功能时钟(){
const[time,setTime]=React.useState(0);
React.useffect(()=>{
const timer=window.setInterval(()=>{
设置时间(时间+1);
}, 1000);
return()=>{
窗口清除间隔(计时器);
};
}, []);
返回(
秒:{time}
);
}
ReactDOM.render(,document.querySelector('#app')代码>
原因是传递到setInterval
的闭包中的回调只在第一次渲染中访问time
变量,它在随后的渲染中无法访问新的time
值,因为useffect()
没有在第二次调用
time
在setInterval
回调中始终具有值0
与您熟悉的setState
一样,状态挂钩有两种形式:一种是在更新状态下使用,另一种是在其中传递当前状态的回调形式。您应该使用第二种形式并读取setState
回调中的最新状态值,以确保在递增之前具有最新状态值
奖励:替代方法
Dan Abramov深入讨论了使用带有钩子的setInterval
,并提供了解决此问题的其他方法。强烈推荐阅读
功能时钟(){
const[time,setTime]=React.useState(0);
React.useffect(()=>{
const timer=window.setInterval(()=>{
设置时间(prevTime=>prevTime+1);//{
窗口清除间隔(计时器);
};
}, []);
返回(
秒:{time}
);
}
ReactDOM.render(,document.querySelector(“#app”);
useffect
当提供空输入列表时,在组件安装时只对功能进行一次评估
setInterval
的另一种方法是在每次更新状态时使用setTimeout
设置新的间隔:
const [time, setTime] = React.useState(0);
React.useEffect(() => {
const timer = setTimeout(() => {
setTime(time + 1);
}, 1000);
return () => {
clearTimeout(timer);
};
}, [time]);
setTimeout
对性能的影响不大,通常可以忽略。除非组件对新设置的超时造成不良影响的时间点敏感,否则setInterval
和setTimeout
方法都是可以接受的。另一种解决方案是使用useReducer
,因为它将始终被传递到当前状态
功能时钟(){
const[time,dispatch]=React.useReducer((状态=0,动作)=>{
如果(action.type==='add')返回状态+1
返回状态
});
React.useffect(()=>{
const timer=window.setInterval(()=>{
分派({type:'add'});
}, 1000);
return()=>{
窗口清除间隔(计时器);
};
}, []);
返回(
秒:{time}
);
}
ReactDOM.render(,document.querySelector(“#app”);
告诉React在时间改变时重新渲染
功能时钟(){
const[time,setTime]=React.useState(0);
React.useffect(()=>{
const timer=window.setInterval(()=>{
设置时间(时间+1);
}, 1000);
return()=>{
窗口清除间隔(计时器);
};
},[时间];
返回(
秒:{time}
);
}
ReactDOM.render(,document.querySelector(“#app”);
这个解决方案不适合我,因为我需要获取变量并做一些事情,而不仅仅是更新它
我得到了一个变通方法,可以通过承诺获得钩子的更新值
例如:
有了这个,我可以像这样得到setInterval函数中的值
let dateFrom = await getCurrentHackValue(setSelectedDateFrom);
正如其他人所指出的,问题在于useState
只调用一次(作为deps=[]
)来设置间隔:
React.useEffect(() => {
const timer = window.setInterval(() => {
setTime(time + 1);
}, 1000);
return () => window.clearInterval(timer);
}, []);
然后,每次setInterval
滴答声时,它实际上会调用setTime(time+1)
,但time
将始终保持最初定义setInterval
回调(closure)时的值
您可以使用useState
的setter的替代形式,并提供回调,而不是要设置的实际值(就像setState
):
但我鼓励您创建自己的useInterval
hook,这样您就可以通过使用setInterval
来干燥和简化代码,正如Dan Abramov在中所建议的:
函数useInterval(回调、延迟){
const intervalRef=React.useRef();
const callbackRef=React.useRef(回调);
//记住最近的回调:
//
//如果不这样做,如果更改回调,当setInterval再次勾选时,它将
//仍将调用您以前的回调。
//
//如果您将'callback'添加到useEffect的deps中,它将正常工作,但是
//间隔将被重置。
React.useffect(()=>{
callbackRef.current=回调;
},[callback]);
//设置时间间隔:
React.useffect(()=>{
如果(延迟类型=='number'){
intervalRef.current=window.setInterval(()=>callbackRef.current(),延迟);
//如果卸载组件或延迟发生变化,则清除间隔:
return()=>window.clearInterval(intervalRef.current);
}
},[延迟];
//如果要手动清除间隔ID,则返回间隔ID的引用:
返回intervalRef;
}
常数时钟=()=>{
const[time,setTime]=React.useState(0);
常量[isPaused,setPaused]=React.useState(false);
const intervalRef=useInterval(()=>{
如果(时间<10){
设置时间(时间+1);
}否则{
窗口清除间隔(intervalRef.current);
}
},isPaused?空:1000);
返回(
setPaused(prevIsPaused=>!prevIsPaused)}禁用={time==10}>
{isPaused?'继续吗⏳' : '暂停按以下步骤操作,效果良好
const [count , setCount] = useState(0);
async function increment(count,value) {
await setCount(count => count + 1);
}
//call increment function
increment(count);
useRef可以解决这个问题,这里有一个
setTime(prevTime => prevTime + 1);
const [count , setCount] = useState(0);
async function increment(count,value) {
await setCount(count => count + 1);
}
//call increment function
increment(count);
import { useState, useEffect, useRef } from "react";
export default function App() {
const initalState = 0;
const [count, setCount] = useState(initalState);
const counterRef = useRef(initalState);
useEffect(() => {
counterRef.current = count;
})
useEffect(() => {
setInterval(() => {
setCount(counterRef.current + 1);
}, 1000);
}, []);
return (
<div className="App">
<h1>The current count is:</h1>
<h2>{count}</h2>
</div>
);
}