【问题标题】:NextJs: Calling setInterval with a variable for time inside of a React useEffect HookNextJs:在 React useEffect Hook 内使用变量调用 setInterval
【发布时间】:2021-07-04 17:40:21
【问题描述】:

我有一个名为 fetchData 的函数。其目的是根据 api 端点参数检索数据并根据间隔参数更新该数据。

import { useState, useEffect } from 'react';

export const fetchData = (endpoint, interval) => {
  const [jsonData, setJsonData] = useState(null);

  useEffect(() => {
    const innerFetchData = async () => {
      fetch(endpoint, {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
        },
      })
        .then((res) => res.json())
        .then((data) => data)
        .then((moreData) => setJsonData(moreData))
        .catch((e) => console.error(e));
    };

    innerFetchData();

    const timer = setInterval(() => {
      innerFetchData();
    }, interval);
    return () => clearInterval(timer);
  }, []);

  return jsonData;
};

我正在导入它并在我的一个反应组件中使用它

const data = fetchData('/api/CustomerWorkspace', 5000);

出了什么问题,我认为它没有正确读取间隔参数,当我运行它时,它可能每秒无限调用 api 50 次。如果我在 fetchData 内部的计时器上硬编码间隔时间,它可以正常工作。你能帮我弄清楚如何传入一个区间变量,以便我可以在不同的组件中重用 fetchData 吗?谢谢。

【问题讨论】:

    标签: reactjs react-hooks next.js setinterval use-effect


    【解决方案1】:

    如果您只在 setInterval 内调用 innerFetchData();,那么它会正常工作。你在 useEffect 中调用了两次innerFetchData()

    【讨论】:

    • 我不明白为什么会这样,但这确实是一个奇怪的设置。无论如何,仅仅删除它并不能完全修复,因为现在它只在间隔中运行,所以,不要在第一次渲染中运行。你不小心删除了代码的一个重要行为。
    【解决方案2】:

    尝试使用 useInterval,这是一个自定义钩子,可以处理此浏览器 API 和 React 的一些奇怪问题。

    短:https://www.30secondsofcode.org/react/s/use-interval

    完整解释:https://overreacted.io/making-setinterval-declarative-with-react-hooks/

    已应用演示:

    // file: useInterval.js
    export const useInterval = (callback, delay) => {
      const savedCallback = React.useRef();
    
      React.useEffect(() => {
        savedCallback.current = callback;
      }, [callback]);
    
      React.useEffect(() => {
        function tick() {
          savedCallback.current();
        }
        if (delay !== null) {
          let id = setInterval(tick, delay);
          return () => clearInterval(id);
        }
      }, [delay]);
    };
    
    // your file
    // import { useEffect, useState, useCallback } from "react"
    // import { useInterval } from "./useInterval"
    export const fetchData = (endpoint, interval) => {
      const [jsonData, setJsonData] = useState(null);
      
      const innerFetchData = useCallback(
        // if you gonna use `.then` you don't need the async in here tho
        async () => {
          fetch(endpoint, {
            method: 'GET',
            headers: {
              'Content-Type': 'application/json',
            },
          })
            .then((res) => res.json())
            .then((data) => data)
            .then((moreData) => setJsonData(moreData))
            .catch((e) => console.error(e));
        },
        [endpoint]
      );
    
      // runs once
      useEffect(() => {
        innerFetchData();
      }, []);
      
      // runs every `interval`
      useInterval(() => {
        innerFetchData();
      }, interval);
    
      return jsonData;
    };

    如果你愿意,你也可以把这个 fetchData 函数放在一个钩子里,只要调用它useFetchData,用法是一样的。不要改变我认为的任何东西,但我认为它更花哨的 xP

    【讨论】:

      【解决方案3】:

      每次调用innerFetchData(),它都会通过setJsonData 设置状态。这会触发重新渲染,导致 useEffect 删除您已经设置的间隔并立即创建一个新的。因此,您的时间间隔可能设置正确,但在每次请求时都会被清除。

      您应该将计时器存储在useRef 中。这将有助于它在组件的整个生命周期中持续存在。

      【讨论】:

        猜你喜欢
        • 2019-12-28
        • 1970-01-01
        • 2023-04-01
        • 2021-04-16
        • 2019-10-08
        • 2021-09-10
        • 1970-01-01
        • 1970-01-01
        • 2021-02-11
        相关资源
        最近更新 更多