首页 > 解决方案 > 使用 react-promise-tracker 的无限获取请求

问题描述

我正在使用 fetch API 从外部 API 获取数据。我想在请求获取数据时实现加载游标。为此,我正在尝试使用 react-promise-tracker 库。但是当使用库中的 trackPromise 时,获取会无限重复。

在此示例项目中更改注释代码时,您可以看到自己发生的错误(检查控制台,这一切都发生在 console.log 中): https ://codesandbox.io/s/tender-easley-szfxg?file=/src /App.tsx

基本上这有效:

export default function App() {
  const get = (url: string) => {
    let headers: {
      Accept: string;
      "Content-Type": string;
    } = {
      Accept: "application/ld+json",
      "Content-Type": "application/json"
    };
   
    return fetch(url, {
      method: "GET",
      headers
    } as RequestInit)
      .then((res: Response) => {
        if (res.ok) {
          return res.json();
        }

        return Promise.reject(res);
      })
      .catch(Promise.reject.bind(Promise));
  };
  get("https://608bb5b6737e470017b752e2.mockapi.io/users")
    .then(console.log)
    .catch(console.log);

  return <div>nothing</div>;
}

这不会:

import { usePromiseTracker, trackPromise } from "react-promise-tracker";   // ADDED CODE

export default function App() {
  const { promiseInProgress } = usePromiseTracker({ area: "fetchDataGet" });      // ADDED CODE
  const cursorStyle = () => {                                                     // ADDED CODE
    return promiseInProgress ? { cursor: "wait" } : undefined;                    // ADDED CODE
  };                                                                              // ADDED CODE
  const get = (url: string) => {
    let headers: {
      Accept: string;
      "Content-Type": string;
    } = {
      Accept: "application/ld+json",
      "Content-Type": "application/json"
    };
    return trackPromise(                                                           // ADDED CODE
      fetch(url, {
        method: "GET",
        headers
      } as RequestInit)
        .then((res: Response) => {
          if (res.ok) {
            return res.json();
          }

          return Promise.reject(res);
        })
        .catch(Promise.reject.bind(Promise)),
      "fetchDataGet"                                                               // ADDED CODE
    );
  };
  get("https://608bb5b6737e470017b752e2.mockapi.io/users")
    .then(console.log)
    .catch(console.log);

  return <div style={cursorStyle()}>nothing</div>;
}

第二个代码的输出是几个 console.log 当它应该只有 1 并且在某些时候服务器返回一个 429 错误(这很正常,这只是服务器安全)

标签: javascriptreactjspromisefetch

解决方案


在您的第二个示例中,您没有使用useEffect而是get()在组件主体内部调用(这意味着每次组件呈现时都会调用它)。usePromiseTracker将触发状态更改,因此每次您的请求承诺解决时都会重新呈现。这肯定会导致无限循环的获取。

一般来说,您永远不应该无条件地触发组件体内的任何副作用(如获取数据)。它应该始终位于仅在特定时间点运行的效果内或事件处理程序内。

在这种特定情况下,您可以通过扭曲get()调用效果来解决它:

useEffect(() => {
    get("https://608bb5b6737e470017b752e2.mockapi.io/users")
    .then(console.log)
    .catch(console.log);
}, []); // will only be called once after mounting

如果你的项目中经常有这种代码,你应该提取一个自定义钩子,它在内部执行此操作并隐藏usePromiseTracker,这样你就不必在每次想要获取某些东西时显式使用它:

const useFetch = (area, url, options) => {
    const { promiseInProgress } = usePromiseTracker({area});

    useEffect(() => {
        trackPromise(                                                           
            fetch(url, options)
                .then(res => res.ok ? res.json() : Promise.reject(res))
                .catch(Promise.reject.bind(Promise)),
            area,                                                               
        );
    }, []);

    return promiseInProgress;
};

推荐阅读