useRequst 文档: https://hooks.umijs.org/zh-CN...Umi Hooks Github 地址:https://github.com/umijs/hooksjavascript
useRequest 是一个超级强大,且生产完备的网络请求 Hooks,目前已经成为蚂蚁中台最佳实践内置网络请求方案。在蚂蚁内部中台应用,写网络请求,都推荐用 useRequest。前端
useRequest 多是目前社区中最强大,最接地气的请求类 Hooks 了。能够覆盖 99% 的网络请求场景,不管是读仍是写,不管是普通请求仍是分页请求,不管是缓存仍是防抖节流,统统都能支持。只有你想不到,没有它作不到(吹牛🐂~)。java
在组件开发中,要实现一个健壮的网络请求,并非一个简单的事情。正如我上一篇文章《Umi Hooks - 助力拥抱 React Hooks》举的例子,实现一个网络请求,咱们须要考虑 loading、竞态处理、组件卸载等等方面。ios
固然经过 React Hooks 的逻辑封装能力,咱们能够将网络请求相关的逻辑封装起来。Umi Hooks 中的 useAsync 就作了这个事情,一行代码就能够实现网络请求,提效很是明显。git
但平常工做中,只用一个 useAsync 仍是不够的,Umi Hooks 中和网络请求相关的 Hooks 就有很是多。好比和分页请求相关的 usePagination,请求自带防抖的 useSearch,内置 umi-request 的 useAPI,加载更多场景的 useLoadMore,等等等等。github
目前已有 Hooks 有几个很明显的缺点:面试
同时随着 zeit/swr 的诞生,给了咱们不少灵感,原来网络请求还能够这么玩!swr 有很是多好用,而且咱们想不到的能力。好比:axios
这里我简单科普下 swr。swr 是
stale-while-revalidate
的简称,最主要的能力是:咱们在发起网络请求时,会优先返回以前缓存的数据,而后在背后发起新的网络请求,最终用新的请求结果从新触发组件渲染。swr 特性在特定场景,对用户很是友好。
基于上面两点,通过内部屡次讨论,最终决定,咱们要作一个能力强大,覆盖全部场景的网络请求 Hooks!useRequest 诞生了!它不只囊括了当前 Umi Hooks 中全部和网络请求相关的 Hooks 的能力,也大量借鉴了 swr 的优秀特性,香的不得了。c#
import { useRequest } from '@umijs/hooks'; function getUsername() { return Promise.resolve('jack'); } export default () => { const { data, error, loading } = useRequest(getUsername) if (error) return <div>failed to load</div> if (loading) return <div>loading...</div> return <div>Username: {data}</div> }
这是一个最简单的网络请求示例。在这个例子中 useRequest
接收了一个 Promise 函数。在组件初始化时,会自动触发 getUsername
执行,并自动管理 data
、 loading
、 error
等数据,咱们只须要根据状态来写相应的 UI 实现便可。api
对于“写”请求,咱们通常须要手动触发,好比添加用户,编辑信息,删除用户等等。 useRequest
只须要配置 manual = true
,便可阻止初始化执行。只有触发 run
时才会开始执行。
import { useRequest } from '@umijs/hooks'; export default () => { const { run, loading } = useRequest(changeUsername, {manual: true}) return ( <Button onClick={() => run('new name')} loading={loading}> Edit </Button> ) }
对于须要保持新鲜度的数据,咱们一般须要不断发起网络请求以更新数据。 useRequest
只要配置 poilingInterval
便可自动定时发起网络请求。
import { useRequest } from '@umijs/hooks'; export default () => { const { data } = useRequest(getUsername, { pollingInterval: 1000 }) return <div>Username: {data}</div> }
同时经过设置 pollingWhenHidden
,咱们能够智能的实如今屏幕隐藏时,暂停轮询。等屏幕恢复可见时,继续请求,以节省资源。
固然你也能够经过 run/cancel
来手动控制定时器的开启和关闭。
什么是并行请求?看了下图应该就明白了,也就是同一个接口,咱们须要维护多个请求状态。
示例中的并行请求有几个特色:
useRequest
经过设置 fetchKey
,便可对请求进行分类。相同分类的请求,只会维护一份状态。不一样分类的请求,则会维护多份状态。在下面的代码中,咱们经过 userId
将请求进行分类,同时咱们能够经过 fetches[userId]
拿到当前分类的请求状态!
export default () => { const { run, fetches } = useRequest(deleteUser, { manual: true, fetchKey: id => id, // 不一样的 ID,分类不一样 }); return ( <div> <Button loading={fetches.A?.loading} onClick={() => { run('A') }}>删除 1</Button> <Button loading={fetches.B?.loading} onClick={() => { run('B') }}>删除 2</Button> <Button loading={fetches.C?.loading} onClick={() => { run('C') }}>删除 3</Button> </div> ); };
一般在边输入边搜索的场景中,咱们会用到防抖功能,以节省没必要要的网络请求。经过 useRequest
,只须要配置一个 debounceInterval
,就能够很是简单的实现对网络请求的节流操做。
在下面的例子中,不管调用了多少次 run
,只会在输入中止后,发送一次请求。
import { useRequest } from '@umijs/hooks'; export default () => { const { data, loading, run, cancel } = useRequest(getEmail, { debounceInterval: 500, manual: true }); return ( <div> <Select onSearch={run} loading={loading}> {data && data.map(i => <Option key={i} value={i}>{i}</Option>)} </Select> </div> ); };
节流与防抖是一样的道理,只须要配置了 throttleInterval
,便可实现节流功能。
在前面我讲了什么是 SWR,在 SWR 场景下,咱们会对接口数据进行缓存,当下次请求该接口时,咱们会先返回缓存的数据,同时,在背后发起新的网络请求,待新数据拿到后,从新触发渲染。
对于一些数据不是常常变化的接口,使用 SWR 后,能够极大提升用户使用体验。好比下面的图片例子,当咱们第二次访问该文章时,直接返回了缓存的数据,没有任何的等待时间。同时,咱们能够看到“最新访问时间”在 2 秒后更新了,这意味着新的请求数据返回了。
useRequest
经过配置 cacheKey
,便可进入 SWR 模式,至关简单。
const { data, loading } = useRequest(getArticle, { cacheKey: 'articleKey', });
同时须要注意,同一个 cacheyKey
的数据是全局共享的。经过这个特性,咱们能够实现“预加载”功能。好比鼠标 hover
到文章标题时,咱们即发送读取文章详情的请求,这样等用户真正点进文章时,数据早已经缓存好了。
经过配置 refreshOnWindowFocus
,咱们能够实现,在屏幕从新聚焦或可见时,从新发起网络请求。这个特性有什么用呢?它能够保证多个 tab 间数据的同步性。也能够解决长间隔以后从新打开网站的数据新鲜度问题。
这里借用 swr 的一个图来讲明问题。
考虑到使用便捷性, useRequest
集成了 umi-request。若是第一个参数不是 Promise,咱们会经过 umi-request
来发起网络请求。
固然若是你想用 axios,也是能够的,经过 requstMethod
便可定制你本身的请求方法。
// 用法 1 const { data, error, loading } = useRequest('/api/userInfo'); // 用法 2 const { data, error, loading } = useRequest({ url: '/api/changeUsername', method: 'post', }); // 用法 3 const { data, error, loading } = useRequest((userId)=> `/api/userInfo/${userId}`); // 用法 4 const { loading, run } = useRequest((username) => ({ url: '/api/changeUsername', method: 'post', data: { username }, }));
中台应用中最多的就是表格和表单了。对于一个表格,咱们要处理很是多的请求逻辑,包括不限于:
useRequest
经过配置 paginated = true
,便可进入分页模式,自动帮你处理表格常见逻辑,同时咱们对 antd Table
作了特殊支持,只用简单几行代码,就能够实现下面图中这样复杂的逻辑,提效百倍。
import {useRequest} from '@umijs/hooks'; export default () => { const [gender, setGender] = useState('male'); const { tableProps } = useRequest((params)=>{ return getTableData({...params, gender}) }, { paginated: true, refreshDeps: [gender] }); const columns = []; return ( <Table columns={columns} rowKey="email" {...tableProps}/> ); };
加载更多的场景也是平常开发中常见的需求。在加载场景中,咱们通常须要处理:
useRequest
经过设置 loadMore = true
,便可进入加载更多模式,配合其它参数,能够帮你处理上面全部的逻辑。
const { data, loading, loadMore, loadingMore } = useRequest((d) => getLoadMoreList(d?.nextId, 3), { loadMore: true, cacheKey: 'loadMoreDemoCacheId', fetchKey: d => `${d?.nextId}-`, });
固然我前面也说了, useReqeust
的功能只有你想不到,没有它没有的。哈哈哈~
除了上面的特性,咱们还有一些其它的能力,能够在文档中发现。好比 loadingDelay
。
经过设置 loadingDelay
,延迟 loading
变为 true
的时间,当请求很快响应时,能够有效避免 loading
变化致使的抖动。
虽然 useRequest
的功能不少,也避免不了有些你想用的特性它不支持。但不用担忧,你能够很方面的基于 useRequest
去扩展。咱们的分页模式,及加载更多模式均是基于底层能力扩展实现的。你能够参考它们的代码,实现本身的特有能力。
经过 useRequest
,能够解决平常 99% 的网络请求需求。奥利给!奥利给!
最后打个招聘广告,蚂蚁金服体验技术部招聘前端啦!要求 P6 及以上!有兴趣的同窗能够发简历到
brickspert.fjl@antfin.com
brickspert.fjl@antfin.com
brickspert.fjl@antfin.com
我会帮您跟进面试进度的,期待您的加入~