【问题标题】:React state is not properly updating反应状态没有正确更新
【发布时间】:2021-07-06 04:39:29
【问题描述】:

我正在研究番茄钟,在休息后切换回番茄钟时遇到问题。

const start = () => {
    //this stop function clears the pre-existing timer if there is any.
    stop();

    setTimerOn(true);

    let interval = setInterval(() => {
      setDisplayTime((prev) => {

        //onBreak is false by default
        if (prev <= 0 && !onBreak) {
          setOnBreak(true);
          return breakDuration;
        }
        
        // this if statement is not being executed
        if (prev <= 0 && onBreak) {
          setOnBreak(false);
          return sessionDuration;
        }

        return prev - 1;
      });
    }, 1000);

    //save the timer insatance in react state
    setTimer(interval);
  }; 

每当计时器变为 0 时,我都会更新 onBreak 的状态。但是当我在 react 开发工具中检查 onBreak 的状态时,只有首先执行块,然后状态永远不会改变。代码在执行第一个 if 语句后进入无限循环,只有 breakSession 被一遍又一遍地执行。

【问题讨论】:

  • breakDurationsessionDuration 是什么?
  • 看起来 onBreak 总是假的。可能会发生,因为此函数已关闭初始状态并且始终使用该陈旧状态 (onBreak)。您可能还想在每个渲染周期重新定义此函数。或用于优化 useCallback 与具有 onbreak 的依赖数组。
  • 如果可能的话,您能否向我们提供一些完整组件的额外代码。很难完全理解这里发生了什么。而且您不应该在任意方法上设置时间。在 useEffect(功能组件)或 componentDidMount(类组件)中使用它们。什么是 setTimer(interval)。 let interval = setInterval(...) 返回区间 id 并且它已经在运行了!
  • 我切换到 useEffect 并且它工作得毫无问题。我读了一些提到同样问题的文章。

标签: javascript reactjs react-hooks


【解决方案1】:

您的代码的问题是您传递给setInterval 的函数在创建时保存了onBreak 的值;它无法获得onBreak 的当前状态。 (我假设[onBreak, setOnBreak] = useState()。)

传递可变状态的一种方法是useRef。像这样的:

const [onBreak, setOnBreak] = useState();

// Create a ref whose value is set automatically to onBreak
const onBreakRef = useRef();
useLayoutEffect(() => {
  onBreakRef.current = onBreak;
}, [onBreak]);

const start = () => {
    //this stop function clears the pre-existing timer if there is any.
    stop();

    setTimerOn(true);

    let interval = setInterval(() => {
      setDisplayTime((prev) => {

        if (prev <= 0 && !onBreakRef.current) {
          setOnBreak(true);
          return breakDuration;
        }
        
        if (prev <= 0 && onBreakRef.current) {
          setOnBreak(false);
          return sessionDuration;
        }

        return prev - 1;
      });
    }, 1000);

    //save the timer insatance in react state
    setTimer(interval);
};

不过,这段代码感觉很乱。您能否改为在其他代码中对onBreaktrue 作出反应的代码中执行setOnBreak(false)?那么setInterval代码可以在prev &lt;= 0时无条件地setOnBreak(true)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-04
    • 2020-02-27
    • 2021-04-29
    • 1970-01-01
    • 2020-07-22
    • 2020-03-09
    • 1970-01-01
    相关资源
    最近更新 更多