使用计时器管理 API 调用的 React 钩子

Posted

技术标签:

【中文标题】使用计时器管理 API 调用的 React 钩子【英文标题】:React hooks to manage API call with a timer 【发布时间】:2019-11-16 03:01:33 【问题描述】:

我需要执行一个返回 URL 的 fetch API 调用,对返回的 URL 执行某些操作,然后在 60 秒后刷新 URL。这是我可以在没有钩子的情况下轻松实现的目标,但我想要一个钩子解决方案。

重要提示:我不打算将其重构为多个组件,或者为计时器或 API 调用创建自定义挂钩。

编辑:问题是 - 这是在钩子环境中处理计时器的正确方法吗?有没有更好的办法?

import React,  useState, useEffect  from 'react'
import  post  from 'utils/requests'

const FetchUrl = ( id ) => 
  const [url, setUrl] = useState('')
  let [count, setCount] = useState(0)

  const tick = () => 
    let newCount = count < 60 ? count + 1 : 0
    setCount(newCount)
  

  useEffect(() => 
    const timer = setInterval(() => tick(), 1000)

    if (count === 0) 
      post('/api/return-url/',  id: [id] )
        .then(res => 
          if (res && res.content) 
            setUrl(res.content.url)
          
        )
    

    return () => clearInterval(timer)
  )

  return url ? (
    <span className="btn sm">
      <a href=url target="_blank" rel="noopener noreferrer">go</a>
    </span>
  ) : null


export default FetchUrl

【问题讨论】:

如果您正在寻找关于工作代码的评论,在您的前提下,Code Review 不是更好的选择吗? 好点 - 我在写的时候有更具体的问题,但最终变得更笼统。 is this the correct way to handle,我个人会说不,正如你所做的那样 -> setCount(newCount),这将导致没有明显原因的重新渲染。 您可能想看看这个为setInterval 编写钩子的好资源。它由 Dan Abramov 编写,他是 react 框架的主要贡献者之一,例如redux 的创建者。 overreacted.io/making-setinterval-declarative-with-react-hooks 只需使用setTimeout,您的useEffect 也没有使用它的第二个参数,因此每次都会重新渲染,我建议传递类似 -> [timerDone] 的内容,其中 timerDone 是定时器完成时设置。您可以使用 useState 或 useRef 作为 timerDone 位。 【参考方案1】:

看看这是否适合你。

我会将它分成 2 个useEffect()。一个在第一次渲染后运行(类似于componentDidMount)来设置计时器。以及其他根据计数值进行API调用。

注意:我使用 ref 只是为了区分一个 API 调用和另一个并添加一个数字。

参见下面的 sn-p:

const FetchUrl = ( id ) => 
  const [url, setUrl] = React.useState('');
  const [count, setCount] = React.useState(0);
  const someRef = React.useRef(0);

  const tick = () => 
    //let newCount = count < 60 ? count + 1 : 0
    setCount((prevState) => prevState < 60 ? prevState +1 : 0);
  
  
  function mockAPI() 
    return new Promise((resolve,request) => 
      someRef.current = someRef.current + 1;
      setTimeout(()=>resolve('newData from API call ' + someRef.current),1000);
    );
  

  React.useEffect(() => 
    const timer = setInterval(() => tick(), 100);
    return () => clearInterval(timer);
  );
  
  React.useEffect(() => 
    if (count === 0) 
      /*post('/api/return-url/',  id: [id] )
        .then(res => 
          if (res && res.content) 
            setUrl(res.content.url)
          
        )
      */
      mockAPI().then((data) => setUrl(data));
    
  ,[count]);

  return url ? (
    <span className="btn sm">
      <div>count</div>
      <a href=url target="_blank" rel="noopener noreferrer">url</a>
    </span>
  ) : null


ReactDOM.render(<FetchUrl/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>

【讨论】:

以上是关于使用计时器管理 API 调用的 React 钩子的主要内容,如果未能解决你的问题,请参考以下文章

React Hooks-处理异步api调用

Vue——生命周期和钩子函数的一些理解

如何在useEffect(func,[])中使用异步api调用中的setState钩子测试组件

React - 是不是在 useEffect 中调用 API

通过索引选择数组中的项目,同时使用 React 钩子

面试官:如何解决React useEffect钩子带来的无限循环问题