自己动手一步步实现一个 umi 的 useRequest

简介

在这里插入图片描述

useRequest 是一个强大的异步数据管理的 Hooks,React 项目中的网络请求场景使用 useRequest 就够了。

useRequest 通过插件式组织代码,核心代码极其简单,并且可以很方便的扩展出更高级的功能。目前已有能力包括:

  • 自动请求/手动请求
  • 轮询
  • 防抖
  • 节流
  • 屏幕聚焦重新请求
  • 错误重试
  • loading delay
  • SWR(stale-while-revalidate) 缓存

具体看官方文档

https://ahooks.js.org/zh-CN/hooks/use-request/index

手写 useRequest

  • 参考GPT 感觉还不错写的
    下面是一个简单的 useRequest 自定义 Hook,包含 run 方法:
import { useState } from "react";

function useRequest(requestFunc) {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [data, setData] = useState(null);

  async function run(...args) {
    setLoading(true);
    setError(null);
    setData(null);

    try {
      const result = await requestFunc(...args);
      setData(result);
    } catch (e) {
      setError(e);
    } finally {
      setLoading(false);
    }
  }

  return {
    loading,
    error,
    data,
    run,
  };
}

useRequest 接受一个请求函数 requestFunc 作为参数,返回一个包含 loadingerrordatarun 四个属性的对象。其中:

  • loading 表示当前请求是否正在加载;
  • error 表示当前请求是否出错,如果出错则是一个 Error 对象;
  • data 表示请求成功后返回的数据;
  • run 是一个函数,用于执行请求函数并更新 loadingerrordata 状态。

run 函数中,我们首先将 loading 状态设为 true,将 errordata 状态设为 null,然后执行请求函数并等待其返回结果。如果请求成功,则将返回的数据设为 data 状态;如果请求失败,则将错误信息设为 error 状态。最后,无论请求成功或失败,都将 loading 状态设为 false

使用示例:

import { useEffect } from "react";
import useRequest from "./useRequest";

function App() {
  const { loading, error, data, run } = useRequest(fetchData);

  useEffect(() => {
    run();
  }, []);

  if (loading) {
    return <div>Loading...</div>;
  }

  if (error) {
    return <div>Error: {error.message}</div>;
  }

  return <div>Data: {JSON.stringify(data)}</div>;
}

async function fetchData() {
  const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
  const data = await response.json();
  return data;
}

在上面的示例中,我们首先调用 useRequest 自定义 Hook,传入一个请求函数 fetchData。然后在 useEffect 中调用 run 方法,执行初始请求。根据请求的状态,我们在页面中显示不同的内容:如果请求正在加载,显示 “Loading…”;如果请求出错,显示错误信息;如果请求成功,显示返回的数据。

下面是一个带有防抖节流功能的 useRequest 自定义 Hook:

const debounce = (fn, Wait) => {
  let timer;
  return function (...args) {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
    timer = setTimeout(() => {
      fn.apply(this, args);
      timer = null;
    }, Wait);
  };
};

const throttle = (fn, Wait) => {
  let timer = null;
  return function (...args) {
    if (!timer) {
      timer = setTimeout(() => {
        fn.apply(this, args);
        timer = null;
      }, Wait);
    }
  };
};

const _useRequest = (requestFn, options = {}) => {
  const { debounceWait = 0, throttleWait = 0 } = options;
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [data, setData] = useState(null);

  const run = useCallback(
    (params) => {
      setLoading(true);
      setError(null);
      const doRequest = () => {
        requestFn(params)
          .then((res) => setData(res))
          .catch((e) => setError(e))
          .finally(() => setLoading(false));
      };
      if (debounceWait > 0) {
        const debounceRequest = debounce(doRequest, debounceWait);
        debounceRequest();
      } else if (throttleWait > 0) {
        const throttleRequest = throttle(doRequest, throttleWait);
        throttleRequest();
      } else {
        doRequest();
      }
    },
    [requestFn, throttleWait, debounceWait]
  );
  return { loading, error, data, run };
};

这个 useRequest 自定义 Hook 接受两个参数:一个函数 request,用于发起异步请求,以及一个可选的配置对象 options,包含 debounceWaitthrottleWait 两个属性,分别表示防抖和节流的等待时间。默认值为 0,表示不启用防抖节流功能。

useRequest 返回一个包含 loadingerrordatarun 四个属性的对象。其中,loading 表示异步请求是否正在加载,error 表示异步请求是否出错,data 表示异步请求返回的数据,run 是一个函数,用于发起异步请求。根据 debounceWaitthrottleWait 的值,run 函数会启用防抖或节流功能,避免过于频繁的异步请求。

你可能感兴趣的:(javascript,前端,react.js,typescript,antd)