React - useEffect 和 setInterval

时间:2021-07-27 09:12:35

标签: reactjs react-hooks setinterval

我有一个在 setInterval 中带有 useEffect 的函数式组件。 但 loadData 中的值始终等于初始一 (0),尽管渲染正确。

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

    ...
    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);
    } 

    ...

2 个答案:

答案 0 :(得分:1)

在 useState 中使用功能更新:

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

答案 1 :(得分:0)

您可以更改 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 的函数将是一个新的函数引用。使用 loadData 作为您的 useEffect() 钩子的依赖项允许您重新定义间隔以在 loadDataservice 时引用新创建的 value 回调变化。下面的可运行示例:

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