Javascript 在setInterval内使用React状态挂钩时状态未更新

Javascript 在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

我正在试用新的,有一个时钟组件和一个计数器,应该每秒钟增加一次。但是,该值不会增加超过1

功能时钟(){
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>
  );
}