NextJs:在 React useEffect Hook 内使用变量调用 setInterval

Posted

技术标签:

【中文标题】NextJs:在 React useEffect Hook 内使用变量调用 setInterval【英文标题】:NextJs: Calling setInterval with a variable for time inside of a React useEffect Hook 【发布时间】: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 吗?谢谢。

【问题讨论】:

【参考方案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 中。这将有助于它在组件的整个生命周期中持续存在。

【讨论】:

以上是关于NextJs:在 React useEffect Hook 内使用变量调用 setInterval的主要内容,如果未能解决你的问题,请参考以下文章

React/NextJS 使用 UseEffect 错误:渲染的钩子比上一次渲染期间更多

Nextjs 在重新加载页面时获取数据

NextJS Router & Jest:如何模拟路由器更改事件

NextJS 中的 LocalStorage , useEffect 问题

使用 NextJS 路由器时的 useEffect 依赖项

nextjs如何监听页面变化