ReactJS挂钩:自调用超时不会更新状态

时间:2020-04-02 20:21:07

标签: javascript reactjs react-hooks

在使用ReactJS Functional ComponentHooks中,我想调用一个函数,该函数将:

  1. 增加组件状态中的属性
  2. 设置Timeout以在固定的时间内再次调用自身

这是该组件的简化示例:

const Counter = () => {
  const [count, setCount] = useState(0);
  const [counterTimeout, setCounterTimeout] = useState(null);
  const updateCount = () => {
    setCount(count + 1);
    console.log(count);
    setCounterTimeout(window.setTimeout(updateCount, 500));
  };
  useEffect(() => {
    updateCount();
    return window.clearTimeout(counterTimeout);
  }, []);
  return null;
};

我正在苦苦挣扎的是为什么不更新State属性?

此外,加分,如果您可以告诉我如何清除组件卸载中的Timeout(肯定不会那样做);

谢谢❤️

2 个答案:

答案 0 :(得分:0)

这应该有效:

const Counter = () => {
  const [count, setCount] = useState(0);

  const updateCount = () => {
    setCount(count + 1);
  };

  useEffect(() => {
    const interval = setInterval(updateCount, 1000);
    return () => {
      clearInterval(interval);
    };
  }, [count]);
  return <span>{count}</span>;
};

https://codesandbox.io/s/react-hooks-counter-demo-520gr

使用setTimeout。

const Counter = () => {
  const [count, setCount] = useState(0);

  const updateCount = () => {
    setCount(count + 1);
  };

  useEffect(() => {
    const timeout = setTimeout(updateCount, 1000);
    return () => {
      clearTimeout(timeout);
    };
  }, [count]);
  return <span>{count}</span>;
};

答案 1 :(得分:-1)

我已经修复了您的代码,并会尝试解释,希望是正确的。我还添加了使用setInterval的替代方法,而不是重新触发setTimeout

https://codesandbox.io/s/serene-ives-502hz

问题是组件的重新呈现。每次更改属性或状态对象时,都会重新渲染组件,以便再次创建其中的所有对象。 updateCount也是如此。但是useEffect内部的方法仅在其中一个监听参数发生更改时才会重新创建。在您的情况下,该数组为空,因此在安装Counter组件时,它将仅运行一次。因此,由于updateCount,对setCount的引用与重新渲染后在组件内部的引用不同。

要绕过此问题,useRef可以解决。这样可以创建一个“静态”变量。因此,在重新渲染组件时将不会重新创建它。因此,我们可以在此处保留对计时器处理程序的引用,然后调用clearTimeout。接下来是对count的访问。

我不确定此解释是否正确:setCount将不会更新当前的count,而是会创建一个新对象。因此,在调用setCount之后,当前count仍具有相同的旧值。为此,setCount可以通过提供将在内部使用count当前值进行调用的函数以不同的方式调用。要输出console.log,只需添加另一个useEffect,每次count更改时就会通过监听count来调用。

const Counter = () => {
  const [count, setCount] = useState(0);
  const counterTimeout = useRef(null);

  const updateCount = () => {
    setCount(count => count + 1);
    counterTimeout.current = window.setTimeout(updateCount, 500);
  };

  useEffect(() => {
    console.log("count", count);
  }, [count]);

  useEffect(() => {
    updateCount();
    return () => {
      window.clearTimeout(counterTimeout.current);
    };
  }, []);
  return null;
};

为完成此操作,我可以使用相同的方法,但是使用setInterval。因此,间隔处理仅在useEffect内部,因此不需要引用。通过将useCallback用于updateCount,它也有一个小的优化。

const CounterInterval = () => {
  const [count, setCount] = useState(0);

  const updateCount = useCallback(() => {
    setCount(count => count + 1);
  }, [setCount]);

  useEffect(() => {
    console.log("interval count", count);
  }, [count]);

  useEffect(() => {
    const counterTimeout = window.setInterval(updateCount, 500);
    return () => {
      window.clearInterval(counterTimeout);
    };
  }, []);
  return null;
};