使用 ahooks 中的 useRequest 轻松管理Re

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

这是我关于 ahooks - useRequest 系列文章的第一篇,其他两篇请查看:

关于 ahooks

2022年的今天,在 React 中使用 Hook 已经是常规的不能再常规的操作了,我们会大量的通过组合 React 提供的 Hook,创建属于自己业务的专属自定义 Hook,亦或是各种工具 Hook。

阿里前端团队出品的 ahooks 正是这样一套 Hook 工具集,里面提供数十个常用的 Hook,可以极大的方便我们的日常开发。

今天我们要着重介绍的。就是 ahooks 项目中最为重量级的一个 hook ,useRequest。

为什么用 useRequest

为什么要用 useRequest,其实要从状态说起。在过去项目开发中我们经常将 用户 UI 交互状态 与 服务端状态 混为一谈,经常是 Redux 一把嗦,通过 fetch 获取到数据用,使用 useState 将数据作为状态,最终渲染到页面。

这样做看起来并没有什么问题,很多项目也都是这样写的。但是 用户 UI 交互状态 是实时性较强的,是我们作为前端可控的状态,而 服务端状态 则涉及到 http 请求,他是异步的、是不完全可控的。

例如一个网络请求其实不光只有最终的数据这一个状态,还有 loading、error等状态,可能还涉及到请求失败后的 retry,一些服务端状态变更不频繁、但是前端需要频繁调用的,还涉及到数据的状态缓存与共享。

仅仅是上面那我们说的这些就已经涉及了大量的状态管理,这样的状态管理显然是有别于 用户 UI 交互状态 的,因此 SWR、react-query、rtk-query这些专注于服务端状态管理的库应运而生.

如果你在之前接触过 SWR 或者是 react-query,那么你可能会比较好的理解 useRequest,你可以将它看作是一个轻量级的 react-query。

快速上手

我们写一个模拟请求用户信息的组件,我们用普通的方式写的话,大致是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
javascript复制代码// 模拟异步请求
const fetchUserInfo = () => {
 return new Promise((resolve, reject) => {
   setTimeout(() => {
     const random = Math.random();
     if (random > 0.5) {
       resolve({
         name: 'John',
         time: new Date().toLocaleString()
      });
    } else {
       reject('这是一个随机的 Error');
    }
  }, 1000);
})
}

function App() {
 const [data, setData] = useState();
 const request = async () => {
   const resp = await fetchUserInfo();
   setData(JSON.stringify(resp));
}
 return (
   <div className="App">
     <button onClick={request}>Requst</button>
     <div>{data}</div>
   </div>
);
}

就像我们之前说的,这种普通的请求方式缺少 loading、error等状态,我们只需要简单的修改代码就能通过 useRequest 实现。

1
2
3
4
5
6
7
8
9
10
11
javascript复制代码function App() {
 const { data, loading, error, run } = useRequest(fetchUserInfo);
 return (
   <div className="App">
     <button onClick={run}>Requst</button>
    {loading && <div>Loading...</div>}
    {error && <div>Error: {error}</div>}
    {data && <div>{JSON.stringify(data)}</div>}
   </div>
);
}

如你所见,他真的非常简单,我们只需要传入一个 Promise ,剩下的全部交给 useRequest 就可以了!

useRequest 的第一个参数 service 是一个异步函数,在组件初次加载时,会自动触发该函数执行。同时自动管理该异步函数的 loading , data , error 等状态。

手动请求

useRequest 是自定触发请求的,也就说当组件挂载到 DOM 树上后,请求会立即发出。如果我们希望手动触发请求,可以为 useRequest 函数配置第二个参数。

第二参数是一个 options 配置,如果设置了 options.manual = true,则 useRequest 不会默认执行,需要通过 run 来触发执行。

1
2
3
arduino复制代码const { data, loading, error, run } = useRequest(fetchUserInfo, {
   manual: true
});

刷新

不同于 run 函数可以接收参数,refresh 不接受参数,它会自动的使用上一次调用传入的参数来发起请求。

1
2
3
arduino复制代码const { data, loading, error, run, refresh} = useRequest(fetchUserInfo, {
   manual: true
});

取消请求 cancel

一般来说,我们不需要处理取消请求,useRequest 会自动在组件卸载时取消请求。

1
go复制代码const { data, loading, error, cancel} = useRequest(fetchUserInfo);

突变 mutate

突变这个概念在 SWR、react-query 中也是存在的,在 ahooks 中使用它非常简单,我们可以把它看成是一个 setData 函数,它可以修改由 useRequest 返回的 data 状态。

1
kotlin复制代码const { data,  mutate} = useRequest(fetchUserInfo);

它往往被用于乐观更新这样的场景!我会在后续文章中介绍如何使用 ahooks 的 useRequest 实现乐观更新。

options 可配置项简介

除了上面我们提到的 options.manual = true ,用于配置手动执行请求之外,options还有很多可选的配置,接下来我们简单介绍几个常用的配置。

1. 传递参数给 service

上面我们介绍了,参数1 service 一般是一个 结果值为 Promise 的函数,如果我们需要传参给这个函数应该怎么做?

答案还是 options,我们可以配置options.defaultParams 字段,如果只有一个参数,直接赋值即可,多参数则赋值一个元组即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
javascript复制代码function App() {
 const { data, loading, error, run } = useRequest(fetchUserInfo, {
   defaultParams: [
     1, 'unknown'
  ]
});
 return (
   <div className="App">
     <button onClick={() => run(123, 'man')}>Requst</button>
    {loading && <div>Loading...</div>}
    {error && <div>Error: {error}</div>}
    {data && <div>{JSON.stringify(data)}</div>}
   </div>
);
}

2. loading状态延时 loadingDelay

一般我们会通过{loading && <div>Loading...</div>} 这样的代码来展示一段加载中动画,但是有时候在网络良好的状况下这个 loading 状态的持续时间非常短暂,如果这个请求是比较频繁调用的,那么就会不断的闪烁 加载动画,这样的体验是很差的,我们可以通过 loadingDelay 配置当 loading时间超过这个值时才展示loading动画

1
2
3
4
php复制代码const { data, loading, error, run } = useRequest(fetchUserInfo, {
 defaultParams: 222,
 loadingDelay: 300
});

3. 生命周期

useRequest 提供了多个生命周期回调函数:

  1. onBefore?: (params: TParams) => void; //在请求之前
  2. onSuccess?: (data: TData, params: TParams) => void; //请求成功
  3. onError?: (e: Error, params: TParams) => void; //请求失败
  4. onFinally?: (params: TParams, data?: TData, e?: Error) => void; //请求结束
1
2
3
4
5
6
7
javascript复制代码  const { data, loading, error, run, refresh } = useRequest(fetchUserInfo, {
   defaultParams: 222,
   loadingDelay: 300,
   onSuccess: (data) => { console.log('请求成功了'); },
   onError: (err) => { console.log('请求失败了'); },
   onFinally: () => { console.log('请求结束了'); }
});

4. 轮询

我们只需要简单配置一个属性,就可以将请求设置为轮询模式。

1
2
3
go复制代码const { data, loading, error} = useRequest(fetchUserInfo, {
 pollingInterval: 3000,
});

我们还可以通过配置 pollingErrorRetryCount 字段,设置轮询错误重试次数。如果设置为 -1,则无限次,当轮询过程出错到达计数时,停止轮询。

配置 pollingWhenHidden 可以设置在页面隐藏时,是否继续轮询。如果设置为 false,在页面隐藏时会暂时停止轮询,页面重新显示时继续上次轮询。

如果设置 options.manual = true,则初始化不会启动轮询,需要通过 run/runAsync 触发开始。

5. ready

ready 的效果其实有点等同于 run 函数,每当 ready 变成 true 时,就会发起一次请求,调用参数1传入的 Promise 异步函数。需要注意的是有参数的情况,ready是用默认参数发起请求,即使后续多次 改变 ready 的状态,他也是使用我们设置的 options.defaultParams 来传递给 service 的。这一定一定要注意,他的调用效果相当于是拿着默认参数的 refresh!

它相当于一个提供了一个总开关,只有当该条件满足时才允许发送请求,无论时自动请求还是手动请求,都受到该字段限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
javascript复制代码function App() {
 const [ready, { toggle }] = useBoolean(false);
 const { data, loading, error, run, refresh } = useRequest(fetchUserInfo, {
   defaultParams: 222,
   ready,
   onSuccess: (data) => { console.log('请求成功了'); },
   onError: (err) => { console.log('请求失败了'); },
   onFinally: () => { console.log('请求结束了'); }
});

 return (
   <div className="App">
     <button onClick={() => run(Math.random(), 'man')}>Requst</button>
     <button onClick={refresh}>refresh</button>
     <button onClick={toggle}>toggle</button>
    {loading && <div>Loading...</div>}
    {error && <div>Error: {error}</div>}
    {data && <div>{JSON.stringify(data)}</div>}
   </div>
);
}

6. 依赖刷新

useRequest 提供了一个 options.refreshDeps 参数,当它的值变化后,会重新触发请求。它的效果基本等同于手写 useEffect。

例如:

1
2
3
4
scss复制代码  const [id, setId] = useState(555);
 const { data } = useRequest(() => fetchUserInfo(id), {
   refreshDeps: [id],
});

总结

行文至此,想必你应经对如何使用 useRequest 有了一定的了解,在后续文章中,我会继续介绍 useRequest 中的其他高级用法,例如 swr 缓存、请求的防抖、节流,还有上面我们说的 乐观更新 等等。

PS:再本文中,大写的 SW R特指的由 Next.js 团队推出的 SWR库,小写的 swr 则指的是 stale-while-revalidate 这一概念,请注意区分。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%