每隔x秒轮询api并做出反应

Edu*_*aki 29 polling reactjs

我必须每隔一两秒钟在屏幕上监控一些数据更新信息.我认为使用此实现的方式:

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 }));
  }
Run Code Online (Sandbox Code Playgroud)

这是正确的方法吗?

Dan*_*uze 37

好吧,既然你只有一个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 }));
}
Run Code Online (Sandbox Code Playgroud)

这里的问题是,一旦你的组件卸载了,虽然对你存储的间隔的引用this.timer设置为null,但它还没有停止.即使在卸载了组件之后,该间隔仍将继续调用处理程序,并将尝试setState在不再存在的组件中.

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

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

我希望这有帮助.

  • 如果您演示了如何进行建议的更改,那就太好了。 (7认同)
  • 请小心使用 setInterval() 的异步调用,因为即使它正在等待 API 响应,它也会调用。更安全的调用是使用 setTimeout() 和递归。 (3认同)
  • 此处添加了 @GustavoGarcia 评论的工作示例 - /sf/answers/4419411321/ (2认同)

Gav*_*ide 26

虽然这是一个老问题,但当我搜索 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]);
}
Run Code Online (Sandbox Code Playgroud)

资料来源: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>;
}
Run Code Online (Sandbox Code Playgroud)

  • 我们如何取消 useInterval ?我的意思是在任何情况下停止投票 (6认同)
  • 将时间设置为 0 取消计时器 (2认同)

Vas*_*pal 11

setTimeout您可以使用和的组合clearTimeout

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.
    });
}
Run Code Online (Sandbox Code Playgroud)


小智 5

@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]);
Run Code Online (Sandbox Code Playgroud)

一定要阅读这篇文章,以更好地理解细节——它非常彻底,写得很好!