我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

useRequest()ahooks的成名钩子,使用范围很广。当然,它其实就相当于axios/fetch/jquery.get等等的变种,可以用于react程序和远程服务器端的交互。它的业务范围,类似于:路由loader属性或者useFetcher钩子,甚至覆盖了reactSWR范围。

苏南大叔:React自定义hook,ahooks的useRequest()钩子如何使用? - ahooks-userequest
React自定义hook,ahooks的useRequest()钩子如何使用?(图3-1)

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。本文测试环境:nodejs@20.18.0create-react-app@5.0.1react-router-dom@6.27.0react@18.3.1ahooks@3.8.1。本文描述的来自ahooksuseRequest()

准备工作

本文基础代码,使用create-react-app获得,先安装ahooks,是关键前提条件。

create-react-app test
cd test
npm i ahooks --save

本文使用setTimeout模拟接口请求,具体参考下文:

简单例子

这里有两个例子,一个是官方文档给出的。另外一个是苏南大叔改写的例子。

最简单例子

这个是官方给出的最简单的例子:

api.js:

export function fetchData() {
  return fetch("http://localhost:8888/who").then((response) => {
    if (!response.ok) {
      throw new Error("Failed to fetch data");
    }
    return response.json();
  });
}

app.js:

import React from "react";
import { useRequest } from "ahooks";
import { fetchData } from './api.js';
function App() {
  const { data, loading, error } = useRequest(fetchData);
  if (loading) {
    return <p>Loading...</p>;
  }
  if (error) {
    return <p>Error: {error.message}</p>;
  }
  return (
    <div>
      <h1>Data:</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}
export default App;

useRequest(),默认情况下,就是立即执行的,组件加载和更新的时候,都会加载。然而,官方也提供了功能定制,定制其执行的时机。

修改版demo

苏南大叔根据自己的理解,对官方的入门例子做了一些改编,写了另外一个demo
fetchRemote.js:

// 真实请求
function fetchDataReal() {
  return fetch("http://localhost:3222/get").then(async (response) => {
    if (!response.ok) {
      throw new Error(await response.text());
    }
    return response.json();
  });
}
// 虚拟请求
function fetchDataFake() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() > 0.5) {
        resolve({
          code: 1,
          data: { id: Date.now().toString().slice(-6), name: "苏南大叔" },
        });
      } else {
        reject({ code: 0, message: "发生了错误" });
      }
    }, 3000);
  });
}
export function fetchData(...args) {
  console.log("network", args);
  // 函数目标:
  // 1. 返回json/string:.then({return json})或者resolove(json)
  // 2. 或者 reject({message:"string"})
  // 3. 或者 throw new Error("string")
  // return fetchDataReal();
  return fetchDataFake();
}

App.js:

import React from "react";
import { useRequest } from "ahooks";
import { fetchData } from "./fetchRemote.js";
function Status(props) {
  if (props.loading) {
    return <p>Loading...</p>;
  }
  if (props.error) {
    return <p>Error: {props.error.message}</p>;
  }
}
function App() {
  const { data, loading, error, run } = useRequest(fetchData);
  return (
    <div>
      <button onClick={run} disabled={loading}>
        {loading ? "loading" : "请求数据"}
      </button>
      <Status loading={loading} error={error}></Status>
      <pre>{!loading && !error && JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}
export default App;

和官方的例子也差不多,只是融合进去了自己对虚拟请求的代码。参考文章:

两点体会

1、要想useRequest()error状态正确呈现,那么相关的远程函数里面的错误就要及时的throw或者reject()。绝对不能catch,否则error的状态就很难得到保证了。

2、它这个data似乎是有些问题的,第二个请求如果失败的话,也不会清空第一次请求的结果data,会造成比较大的误会。所以,需要仔细判断处理。

苏南大叔:React自定义hook,ahooks的useRequest()钩子如何使用? - 修改版demo
React自定义hook,ahooks的useRequest()钩子如何使用?(图3-2)

核心代码 / 参数传递

主要的核心内容,就是这里了。

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

这里的重点就是函数fetchData,注意:这里没有(),没有传递参数。所以,对于传递参数的需求,个人建议可以略作修改,比如:

const { data, loading, error, run } = useRequest( () => fetchData(123) );

或者:

const fetchDataGo = () => {
  fetchData(123);
}
//...
const { data, loading, error, run } = useRequest(fetchDataGo);

因为上面的run,也代表着这个取数据的函数。所以,最好把带参数的函数调用,再做一次封装。免得发生混乱。

各种选项

官方提供了很多选项,功能相当详细,各种细节都有考虑到,不过苏南大叔都用不到。useRequest()官方文档如下:

不默认执行

const { data, loading, error, run } = useRequest(fetchData,{
  loadingDelay: 300,
  manual: true,
  // refreshDeps: [uid], // 和手工选项冲突
});

因为这个函数是默认执行的,组件加载或刷新的时候,就会默认发出网络请求。所以,加个手动开关还是比较有必要的。比如:manual: true。其实,就是不默认执行的意思。和run或者runsync是否生效无关。

至于runrunsync这两个傀儡函数,苏南大叔的理解就是使用run吧。毕竟和官方的思路最契合,官方对error做管理。

loading状态延迟

loadingDelay,官方说法叫做“防闪烁”。如果网络数据接口非常快就返回数据的话,界面就会闪烁。其实就是切换到loading状态,又马上切换走了。设置这个值,就可以延迟loading状态的切换。

经过测试,苏南大叔发现这个东东就是个掩耳盗铃的设置。仅仅是延迟变成loading的状态,实际请求早已发生。因为界面上一些组件也是依赖这个loading状态进行表现的。所以,延迟loading,就会使得其它的一些组件延迟刷新了。体验也不一定好。

依赖项refreshDeps

这个设置还是比较有意思的,但是开启它的前提就是manual: false。虽然官方没说这事,但是经过多次实验也是可以知道的。关联的参数,也必须是useState的。否则没效果。估计useReactive的变量,应该也行。

import React, { useState } from "react";
import { useRequest } from "ahooks";
import { fetchData } from "./fetchRemote.js";

function Status(props) {
  if (props.loading) {
    return <p>Loading...</p>;
  }
  if (props.error) {
    return <p>Error: {props.error.message}</p>;
  }
}

function App() {
  const [uid, setUid] = useState(1);
  const { data, loading, error, run } = useRequest(() => fetchData(uid), {
    loadingDelay: 300,
    refreshDeps: [uid], // 和手工选项冲突
  });
  return (
    <div>
      <button
        onClick={() => {
          setUid(uid + 1);
        }}
        disabled={loading}
      >
        设置uid,{uid}
      </button>
      <Status loading={loading} error={error}></Status>
      <pre>{!loading && !error && JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}
export default App;

苏南大叔:React自定义hook,ahooks的useRequest()钩子如何使用? - 关联依赖
React自定义hook,ahooks的useRequest()钩子如何使用?(图3-3)

结语

远程数据请求,总是一个老生常谈,还每次都能谈出新花样的问题。更多react经验文章,请参考:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   react