反应状态反复恢复到旧值

React state repeatedly reverts back to old value

有一个组件 "DateForm" 在提交表单时更改 "counterInfo" 的全局状态。

//DateForm component submittal function. 
const submitDate = () =>{
    props.setCounterInfo(dateInfo); //passes date info to be counterInfo state in App.js            
    props.setShowInputForm(false);  //DateInfo component is no longer rendered
}

然后,在 app.js 中,counterInfo 状态被传递给 Timer 组件

const App = () => {
  const [showInputForm, setShowInputForm] = useState(false);
  const [counterInfo, setCounterInfo] = useState(undefined);
  return (
    <>
      <Timer
      counterInfo = {counterInfo}
      ></Timer>
      {showInputForm && 
      <DateForm
      setShowInputForm = {setShowInputForm}
      setCounterInfo = {setCounterInfo}
      ></DateForm>}
    </>
  );
}

Timer 函数内部有一个 useEffect 挂钩,它每隔一秒使用 counterInfo 的值。

//Inside the Timer Component
const [currTime, setCurrTime] = useState(null);
useEffect (() => {
    setInterval(() => {
        let timeLeft = (new Date(`${Months(props.counterInfo.year)[props.counterInfo.month-1].name} ${props.counterInfo.day} ${props.counterInfo.year} ${props.counterInfo.hour}:${props.counterInfo.minute}:${props.counterInfo.second}`).getTime()) - new Date().getTime();
        setCurrTime(timeLeft);
    },1000);
    return(clearInterval());
}, [props, setCurrTime]);

我打算在 DateForm 中更新 counterInfo 的值时更新 Timer.js 中的 timeLeft 值,但是,当在 DateForm 中更改值时,新的结果在Timer.js中使用timeLeft的值时,counterInfo的值和旧值都会闪烁。此问题不是由 Timer.js 中的任何代码引起的,因为我尝试将 useEffect 挂钩移至 app.js 并将值传递给 Timer,但问题仍然存在。 setCounterInfo 状态唯一改变的地方是在 DateForm 组件中。 有人知道如何解决这个问题吗?

首先,您在 interval decleration

处有一点语法错误
  useEffect (() => {
       let interval = setInterval(() => {...},1000);
       return () => clearInterval(interval);
   }, [props, setCurrTime]);

但不相关,React 默认情况下会在每次渲染后重新应用效果。这是有意的,有助于避免 React 组件中存在的全部 class 错误。

当谈到间隔时,如果每次调用 setInterval 时都应用渲染,它的具体问题是,它永远不会有机会实际 运行

换句话说,此代码可能会产生一些副作用,因为 useEffect 在每个 运行 中只关心当时的现有值而忘记其他所有内容,而 interval 是像这样。

为此,从我的角度来看,最佳做法是创建 useInterval 自定义挂钩,其中将同时存储回调


function useInterval(callback) {
 const savedCallback = React.useRef();

 useEffect(() => {
   savedCallback.current = callback;
 });


 useEffect(() => {
   function run() {
     savedCallback.current();
  }
   let interval = setInterval(run ,1000);
   return () => clearInterval(interval);
  }, [])
 }



//Inside the Timer Component
 const [currTime, setCurrTime] = useState(null);

 useInterval(()=>
     setCurrTime((new Date(`${Months(props.counterInfo.year)[props.counterInfo.month-1].name} ${props.counterInfo.day} ${props.counterInfo.year} ${props.counterInfo.hour}:${props.counterInfo.minute}:${props.counterInfo.second}`).getTime()) - new Date().getTime()))