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
我會幫您跟進面試進度的,期待您的加入~