【问题标题】:React - useEffect with setInterval反应 - 使用 setInterval 的 useEffect
【发布时间】:2021-10-03 02:45:38
【问题描述】:

我有一个带有setIntervaluseEffect 内的功能组件。 但loadData 中的值始终等于初始一 (0),尽管渲染正确。

如何在setInterval 调用的loadData 中获取当前的value

    ...
    const [value, setValue] = useState(0);
    const [service, setService] = useState();

    useEffect(() => {
        setService(new ApiService());
    }, [])

    useEffect(
        () => {
            const timerId = setInterval(loadData, 1000 * 5);
            return () => clearInterval(timerId);
        }, 
        [service]
    )

    async function loadData() {
        const result = await service.getData();
        console.log("old value: ", value); // "value" is always 0 (initial)
        setValue(result.data.value);
    } 

    ...

【问题讨论】:

    标签: reactjs react-hooks setinterval


    【解决方案1】:

    在 useState 中使用功能更新:

    setValue((prevState) => {
      console.log("old value: ", prevState);
      return result.data.value;
    });
    

    【讨论】:

      【解决方案2】:

      您可以更改 useEffect() 挂钩的依赖项数组,此时由于 loadData 关闭 value,您最终会处于陈旧状态:

      const loadData = useCallback(async function() {
        const result = await service.getData();
        console.log("old value: ", value); // "value" is always 0 (initial)
        setValue(result.data.value);
      }, [service, value]);
      
      
      useEffect(() => {
        const timerId = setInterval(loadData, 1000 * 5);
        return () => clearInterval(timerId);
      }, [loadData]);
      

      上面将你的函数包装在钩子useCallback() 中,如果依赖数组:servicedata 包含与先前渲染相同的值,则返回相同的函数引用。如果依赖数组中的值发生变化,那么分配给loadData 的函数将是一个新的函数引用。当servicevalue 发生变化时,使用loadData 作为useEffect() 挂钩的依赖项允许您重新定义间隔以引用新创建的loadData 回调。下面的可运行示例:

      <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
      <script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script>
      
      <script type="text/babel">
        const {useState, useEffect, useCallback} = React;
      
        const getRandomAsync = () => new Promise((resolve) => { // simulate api call
          setTimeout(resolve, 10, Math.floor(Math.random() * 100));
        });
      
        const App = () => {
          const [value, setValue] = useState(0);
      
          const loadData = useCallback(async () => {
            const num = await getRandomAsync();
            console.log("Got random number:", num);
            console.log("Value currently is:", value);
            setValue(num);
          }, [value]);
      
          useEffect(() => {
            const timerId = setInterval(loadData, 1000);
            return () => clearInterval(timerId);
          }, [loadData]);
      
          return <p>{value}</p>;
        }
        
        ReactDOM.render(<App />, document.body);
      </script>

      【讨论】:

      • 现在不是每次服务或值更改时都会调用“setInterval”吗?我宁愿仅在组件安装时触发间隔,这样您也可以防止任何内存泄漏。
      • 是的,但是service 已经是 OPs 问题中 useEffect 挂钩的依赖项,因此行为是相同的。带有 clearInterval() 的清理函数应该处理清理旧的运行间隔,这将允许执行引用新的 loadData 函数的新间隔。根据 OP 尝试做的事情,在你展示的 setter 中使用 prevState 可能是一个更好的选择
      猜你喜欢
      • 2021-07-31
      • 2020-09-10
      • 2019-09-09
      • 2021-02-23
      • 2020-05-07
      • 2021-05-11
      • 2022-08-05
      • 2020-11-25
      • 1970-01-01
      相关资源
      最近更新 更多