每 x 秒轮询一次 API 并做出react

IT技术 javascript reactjs polling
2021-03-27 11:39:25

我必须每隔一两秒监视屏幕上的一些数据更新信息。我认为使用这个实现的方式是:

    componentDidMount() {
        this.timer = setInterval(()=> this.getItems(), 1000);
      }
    
      componentWillUnmount() {
        this.timer = null;
      }
    
      getItems() {
        fetch(this.getEndpoint('api url endpoint"))
            .then(result => result.json())
            .then(result => this.setState({ items: result }));
      }

这是正确的方法吗?

6个回答

好吧,由于您只有一个 API 并且无法控制它以将其更改为使用套接字,因此您拥有的唯一方法就是轮询。

根据您的民意调查,您正在采取体面的方法。但是上面的代码中有一个问题。

componentDidMount() {
  this.timer = setInterval(()=> this.getItems(), 1000);
}

componentWillUnmount() {
  this.timer = null; // here...
}

getItems() {
  fetch(this.getEndpoint('api url endpoint"))
    .then(result => result.json())
    .then(result => this.setState({ items: result }));
}

这里的问题是,一旦您的组件卸载,尽管您存储的时间间隔的引用this.timer设置为null,但它尚未停止。即使在您的组件已卸载后,该时间间隔仍将继续调用处理程序,并将尝试setState在不再存在的组件中调用

要正确处理它,clearInterval(this.timer)请先使用然后设置this.timer = null

此外,fetch调用是异步的,这可能会导致相同的问题。使其可取消,如果有任何fetch不完整取消

我希望这有帮助。

此处添加了@GustavoGarcia 评论的工作示例 - stackoverflow.com/a/63134447/5618143
2021-05-24 11:39:25
小心使用 setInterval() 进行异步调用,因为即使它正在等待 API 响应,它也会调用。更安全的调用是使用带有递归的 setTimeout()。
2021-05-28 11:39:25
如果您能演示如何进行您提议的更改,那就太好了。
2021-06-10 11:39:25

虽然这是一个老问题,但当我搜索 React Polling 并且没有与 Hooks 一起工作的答案时,它是最高结果。

// utils.js

import React, { useState, useEffect, useRef } from 'react';

export const useInterval = (callback, delay) => {

  const savedCallback = useRef();

  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);


  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      const id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

资料来源:https : //overreacted.io/making-setinterval-declarative-with-react-hooks/

然后您就可以导入和使用了。

// MyPage.js

import useInterval from '../utils';

const MyPage = () => {

  useInterval(() => {
    // put your interval code here.
  }, 1000 * 10);

  return <div>my page content</div>;
}
轮询后获得想要的响应后如何取消计时器?试图添加0延迟。它不断调用 API。
2021-05-25 11:39:25
如何在typescript中制作这个?
2021-05-26 11:39:25
将时间设置为 0 将取消计时器
2021-06-13 11:39:25
这个钩子的typescript NPM 包:npmjs.com/package/@use-it/interval
2021-06-13 11:39:25
我们如何取消 useInterval ?我的意思是在任何情况下停止投票
2021-06-17 11:39:25

你可以使用的组合setTimeoutclearTimeout

setInterval无论上次调用是成功还是失败,都会每“x”秒触发一次 API 调用。这可能会占用您的浏览器内存并随着时间的推移降低性能。而且,如果服务器宕机,setInterval会在不知道其宕机状态的情况下继续轰炸服务器。

然而,

您可以使用setTimeout. 仅当前一个 API 调用成功时才触发后续 API 调用。如果之前的调用失败,清除超时并且不要触发任何进一步的调用。如果需要,在失败时提醒用户。让用户刷新页面以重新启动此过程。

这是一个示例代码:

let apiTimeout = setTimeout(fetchAPIData, 1000);

function fetchAPIData(){
    fetch('API_END_POINT')
    .then(res => {
            if(res.statusCode == 200){
                // Process the response and update the view.
                // Recreate a setTimeout API call which will be fired after 1 second.
                apiTimeout = setTimeout(fetchAPIData, 1000);
            }else{
                clearTimeout(apiTimeout);
                // Failure case. If required, alert the user.
            }
    })
    .fail(function(){
         clearTimeout(apiTimeout);
         // Failure case. If required, alert the user.
    });
}

@AmitJS94,有一个详细的部分介绍了如何停止添加到 GavKilbride在本文中提到的方法的间隔

作者说要为延迟变量添加一个状态,并在要暂停间隔时为该延迟传入“null”:

const [delay, setDelay] = useState(1000);
const [isRunning, setIsRunning] = useState(true);
  useInterval(() => {
    setCount(count + 1);
  }, isRunning ? delay : null);

    useEffect(() => {
    function tick() {
      savedCallback.current();
    }

    if (delay !== null) {
      let id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);

一定要阅读这篇文章,以更好地了解细节——它超级透彻且写得很好!

正如 Vasanth 提到的,我更喜欢:

import { useEffect, useRef } from 'react';

export const useInterval = (
  callback: Function,
  fnCondition: Function,
  delay: number,
) => {
  const savedCallback = useRef<Function>();
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);
  useEffect(() => {
    let id: NodeJS.Timeout;
    const tick = async () => {
      try {
        const response =
          typeof savedCallback.current === 'function' &&
          (await savedCallback.current());
        if (fnCondition(response)) {
          id = setTimeout(tick, delay);
        } else {
          clearTimeout(id);
        }
      } catch (e) {
        console.error(e);
      }
    };
    tick();
    return () => id && clearTimeout(id);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [delay]);
};

WORKS在里面使用fnCondition,它可以是基于最后一个请求的响应的条件。

//axios-hooks
const {
    data,
    isLoadingData,
    getData,
} = api.useGetData();

const fnCondition = (result: any) => {
    const randomContidion = Math.random();
    //return true to continue
    return randomContidion < 0.9;
  };
useInterval(() => getData(), fnCondition, 1000);

不工作:传递延迟停止useInterval这样不为我工作, 使用此代码:https://www.aaron-powell.com/posts/2019-09-23-recursive-settimeout-with-react-钩子/

(您可能会觉得它有效,但在几次启动/停止后它会中断)

  const [isRunning, setIsRunning] = useState(true);
  const handleOnclick = () => {
    setIsRunning(!isRunning);
  };

  useInterval(() => getData(), isRunning ? 1000 : null);
  <button onClick={handleOnclick}>{isRunning ? 'Stop' : 'Start'}</button>

总结:我可以通过传递 fnCondition 来停止 useInterval,但不能通过传递delay=null

您知道为什么将回调保存在 ref 中吗?
2021-06-20 11:39:25