Axios获取请求如何取消请求

ql3eal8s  于 11个月前  发布在  iOS
关注(0)|答案(2)|浏览(147)

我正在创建一个钩子,使一个API请求获取统计数据。然而,当您浏览月份时,它还没有从上一个请求中检索到数据,它开始获取下一个请求,导致统计数据在不断刷新时跳转。我已经在钩子中添加了cancelToken,但没有成功。
我希望有人能帮助我,告诉我我错过了什么。

钩子代码:

import { atom, useAtom } from "jotai";
import axios from "axios";
import queryString from "query-string";

// Types
// import { MonthData, RevenueFunnelTypes, TotalsData } from "../../lib/types";
import {
  Revenue,
  RevenueQ,
  RevenueFunnelTypes,
  TotalCardsTypes,
} from "../../lib/types";

export type Status = "idle" | "loading";
export type Values = {
  revenueFunnel: RevenueFunnelTypes;
  totals: TotalCardsTypes;
  revenue: Revenue | null;
  revenueQ: RevenueQ | null;
  // month: MonthData[];
};

export const rowsStatusAtom = atom<Status>("idle");

type ApiRowsResponse = {
  body: any;
};

type State = {
  values: Values;
  status: Status;
  actions: {
    get: (
      type: "revenue" | "revenue-quarters" | "revenue-funnel" | "totals",
      date?: string
    ) => Promise<State["values"]>;
  };
};

export interface sortRowsProps {
  id: number;
  order: number;
}

// Services
const createApiUrl = (route?: string, params?: any) => {
  return queryString.stringify({
    path: route,
    ...params,
  });
};

export default function useStats(): State {
  const [values, setValues] = useAtom(valuesAtom);
  const [status, setStatus] = useAtom(rowsStatusAtom);

  const CancelToken = axios.CancelToken;
  const source = CancelToken.source();

  const getCurrentValues = async function (
    type: "revenue" | "revenue-quarters" | "revenue-funnel" | "totals",
    date?: string
  ): Promise<ApiRowsResponse | any> {

    const url = createApiUrl("statistics", {
      type: type,
      date,
    });

    return axios
      .get(`?${url}`, {
        cancelToken: source.token
      })
      .then(function (response) {
        return response.data;
      })
      .catch(function (thrown) {
        if (axios.isCancel(thrown)) {
          console.log('Request canceled', thrown.message);
        } else {
          // handle error
        }
      })
  };

  // Actions
  // Get the current rows from the server
  const get = async (
    type: "revenue" | "revenue-quarters" | "revenue-funnel" | "totals",
    date?: string
  ): Promise<State["values"]> => {
    setStatus("loading");

    const data = await getCurrentValues(type, date);

    if (type === "revenue-funnel") {
      setValues((prevState) => ({
        ...prevState,
        revenueFunnel: data.body,
      }));
    } else if (type === "revenue") {
      setValues((prevState) => ({
        ...prevState,
        revenue: data.body,
      }));
    } else if (type === "revenue-quarters") {
      setValues((prevState) => ({
        ...prevState,
        revenueQ: data.body,
      }));
    } else if (type === "totals") {
      setValues((prevState) => ({
        ...prevState,
        totals: data.body,
      }));
    }

    setStatus("idle");

    return data.body;
  };

  return {
    values,
    status,
    actions: {
      get,
    },
  };
}

导航功能如下:

useEffect(() => {
    if (loading && state === 0) {
      actions.get("totals").then(() => {
        setLoading(false);
      });
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [loading, state]);

  // Functions
  const goToNextMonth = () => {
    setLoading(true);
    const updatedDate = moment(selectedDate).add(1, 'month').format('YYYY-MM-DD');
    setSelectedDate(updatedDate);
    actions.get("totals", updatedDate).then(() => setLoading(false));
  };

  const goToPreviousMonth = () => {
    setLoading(true);
    const updatedDate = moment(selectedDate).subtract(1, 'month').format('YYYY-MM-DD');
    setSelectedDate(updatedDate);
    actions.get("totals", updatedDate).then(() => setLoading(false));
  };

创建一个Sandbox作为演示:https://codesandbox.io/s/axioscancelrequest-jdstpz?file=/src/App.tsx
尽可能低地禁用缓存和节流。

4ioopgfo

4ioopgfo1#

您永远不会取消先前的请求。通过禁用前一个(如果存在)来启动每个请求,生成一个新的令牌源,并将其保存在reftokenSource)上,以便下一个请求可以取消它:

export default function useStats(): State {
  const [posts, setPosts] = useAtom(valuesAtom);
  const [status, setStatus] = useAtom(rowsStatusAtom);
  const tokenSource = useRef<null | CancelTokenSource>(null);

  const getCurrentMovies = async function (): Promise<ApiRowsResponse | any> {
    tokenSource.current?.cancel('Operation canceled by the user.');

    const source = axios.CancelToken.source();

    tokenSource.current = source;

    const returnData = axios
      .get("https://jsonplaceholder.typicode.com/posts", {
        cancelToken: source.token
      })

但是,CancelToken已被弃用,您应该使用标准的AbortController

export default function useStats(): State {
  const [posts, setPosts] = useAtom(valuesAtom);
  const [status, setStatus] = useAtom(rowsStatusAtom);
  const controllerRef = useRef<null | AbortController>(null);

  const getCurrentMovies = async function (): Promise<ApiRowsResponse | any> {
    controllerRef.current?.abort("Operation canceled by the user.");

    const controller = new AbortController();

    controllerRef.current = controller;

    const returnData = axios
      .get("https://jsonplaceholder.typicode.com/posts", {
        signal: controller.signal
      })
jutyujz0

jutyujz02#

根据加载状态,您可以有条件地停止用户调用下一个请求

const goToNextMonth = () => {
  if(!loading){
  setLoading(true);
  const updatedDate = moment(selectedDate).add(1, 'month').format('YYYY-MM-DD');
  setSelectedDate(updatedDate);
  actions.get("totals", updatedDate).then(() => setLoading(false));}
};

相关问题