@umijs/use-api
v0.3.0
Published
React Hooks for fetching, caching and updating asynchronous data
Downloads
2
Maintainers
Keywords
Readme
useAPI
Production-ready React Hooks library for manage asynchronous data.
特性
- 自动请求/手动请求
- SWR(stale-while-revalidate)
- 缓存/预加载
- 屏幕聚焦重新请求
- 轮询
- 防抖
- 节流
- 并行请求
- loading delay
- 分页
- 加载更多,数据恢复 + 滚动位置恢复
- [ ] 错误重试
- [ ] 请求超时管理
- [ ] suspense
- ......
在线体验
快速开始
安装
$ npm i @umijs/use-api --save
# or
$ yarn add @umijs/use-api
使用
//service 为异步函数。
const {data, error, loading} = useAPI(service);
import React from 'react';
import useAPI from '@umijs/use-api';
export default () => {
const { data, error, loading } = useAPI(getUserInfo)
if (error) return <div>failed to load</div>
if (loading) return <div>loading...</div>
return <div>hello {data.username}!</div>
}
在这个例子中, useAPI 接收了一个异步函数 getUserInfo
,在组件初次加载时,自动触发该函数执行。同时 useAPI 会自动管理异步请求的 loading
, data
, error
等状态。
文档
默认请求
如 快速开始 例子一样, useAPI 在组件初始化时会默认执行 service。
手动触发
通过设置 options.manual = true
, 则需要手动调用 run
时才会触发执行。
const { loading, run } = useAPI(changeUsername, {
manual: true,
});
<button onClick={() => run('new name')}>
{loading ? 'Editting...' : 'Edit'}
</button>
突变
你可以通过 mutate
,直接修改 data
。 mutate
函数参数可以为 newData
或 (data)=> newData
。
const { data, mutate } = useAPI(getUserInfo);
<button onClick={() => mutate('new name')}>
Edit
</button>
轮询
通过设置 pollingInterval
,进入轮询模式,定时触发函数执行。同时可以通过设置 pollingWhenHidden = false
,在屏幕不可见时,暂时暂停定时任务。
- 你可以通过
run
/cancel
来 开启/停止 轮询。 - 在
manual=true
时,需要第一次执行run
后,才开始轮询。
const { data, loading } = useAPI(getUserInfo, {
pollingInterval: 1000,
pollingWhenHidden: false
});
防抖
通过设置 debounceInterval
,则进入防抖模式。此时如果频繁触发 run
,则会以防抖策略进行请求。
const { data, loading, run } = useAPI(getUserInfo, {
debounceInterval: 500
});
节流
通过设置 throttleInterval
,则进入节流模式。此时如果频繁触发 run
,则会以节流策略进行请求。
const { data, loading, run } = useAPI(getUserInfo, {
throttleInterval: 500
});
缓存 & SWR
如果设置了 cacheKey
, useAPI 会将当前请求结束数据缓存起来。下次组件初始化时,如果有缓存数据,我们会优先返回缓存数据,然后在背后发送新请求,也就是 SWR 的能力。
const { data, loading, run } = useAPI(getInfo, {
cachekey: 'getInfoKey'
});
预加载
同一个 cacheKey
的请求,是全局共享的,也就是你可以提前加载数据。利用该特性,可以很方便的实现预加载。你可以在线上例子中体验一下。
屏幕聚焦重新请求
如果你设置了 refreshOnWindowFocus = true
,则在浏览器窗口 refouc
和 revisible
时,会重新发起请求。
你可以通过设置 focusTimespan
来设置请求间隔,默认为 5000ms
。
const { data, loading } = useAPI(getUserInfo, {
refreshOnWindowFocus: true,
focusTimespan: 5000
});
并行请求
通过 fetchKey
,可以将请求进行分类,每一类的请求都有独立的状态,你可以在 fetches
中找到所有的请求。
const { run, fetches } = useAPI(disableUser, {
manual: true,
fetchKey: (id) => id,
});
Loading Delay
通过设置 loadingDelay
,可以延迟 loading
变成 true
的时间,有效防止请求抖动。
const withLoadingDelayAction = useAPI(getCurrentTime, {
loadingDelay: 200
});
refreshDeps
当某些 state
变化时,我们需要重新执行异步请求,一般我们会这样写代码:
const [userId, setUserId] = useState('1');
const { data, run, loading } = useAPI(getUserSchool, { manual: true });
useEffect(() => {
run();
}, [userId]);
refreshDeps
是一个语法糖,让你更方便的实现上面的功能。当 refreshDeps
变化时,我们会重新执行异步请求。
const [userId, setUserId] = useState('1');
const { data, run, loading } = useAPI(() => {
return getUserSchool()
}, {
refreshDeps: [userId]
});
基础 API
const {
data,
error,
loading,
run,
params,
cancel,
refresh,
mutate,
fetches,
} = useAPI(service, {
manual,
initialData,
refreshDeps,
onSuccess,
onError,
formatResult,
cacheKey,
loadingDelay,
defaultParams,
pollingInterval,
pollingWhenHidden,
fetchKey,
refreshOnWindowFocus,
focusTimespan,
debounceInterval,
throttleInterval,
});
返回
data: undefined | any
service 返回的数据,默认为
undefined
。- 如果有
formatResult
, 则该数据为被格式化后的数据。
- 如果有
error: undefined | Error
- service 抛出的异常,默认为
undefined
。
- service 抛出的异常,默认为
loading: boolean
- service 是否正在执行。
run: (...args: any[]) => Promise
- 手动触发 service 执行,参数会传递给 service。
params: any[]
- 当次执行的 service 的参数数组。比如你触发了
run(1, 2, 3)
,则 params 等于[1, 2, 3]
- 当次执行的 service 的参数数组。比如你触发了
cancel: () => void
- 取消当前请求。
- 如果有轮询,停止。
refresh: () => void
- 使用上一次的 params,重新执行 service。
mutate: data | (data)=>newData
- 直接修改 data。
fetches: {[key:string]: {loading,data,error,params,cancel,refresh,mutate,run}}
- 默认情况下,新请求会覆盖旧请求。如果设置了
fetchKey
,则可以实现多个请求并行,fetches
存储了多个请求的状态。 - 外层的状态为最新触发的 fetches 数据。
- 默认情况下,新请求会覆盖旧请求。如果设置了
参数
所有的 Options 均是可选的。
manual: boolean
- 默认
false
。 即在初始化时自动执行 service。 - 如果设置为
true
,则需要手动调用run
触发执行。
- 默认
initialData: any
- 默认的 data
refreshDeps: any[]
- 在
manual = false
时,refreshDeps
变化,会触发 service 重新执行。
- 在
formatResult: (response: any) => any
- 格式化请求结果。
onSuccess: (data:any, params: any[]) => void
- service resolve 时触发,参数为
data
和params
- 如果有
formmatResult
,则data
为格式化后数据。
- service resolve 时触发,参数为
onError: (error: Error, params: any[]) => void
- service 报错时触发,参数为
error
和params
。
- service 报错时触发,参数为
fetchKey: (...params: any[]) => string
- 根据 params,获取当前请求的 key,设置之后,我们会在
fetches
中同时维护不同key
值的请求状态。
- 根据 params,获取当前请求的 key,设置之后,我们会在
cacheKey: string
- 请求唯一标识。如果设置了
cacheKey
,我们会启用缓存机制。 - 我们会缓存每次请求的
data
,error
,params
,loading
。 - 在缓存机制下,同样的请求我们会先返回缓存中的数据,同时会在背后发送新的请求,待新数据返回后,重新触发数据更新。
- 请求唯一标识。如果设置了
defaultParams: any[]
- 如果
manual=false
,自动执行run
的时候,默认带上的参数。
- 如果
loadingDelay: number
- 设置显示 loading 的延迟时间,避免闪烁。
pollingInterval: number
- 轮询间隔,单位为毫秒。设置后,将进入轮询模式,定时触发
run
。
- 轮询间隔,单位为毫秒。设置后,将进入轮询模式,定时触发
pollingWhenHidden: boolean
- 在页面隐藏时,是否继续轮询。默认为
true
,即不会停止轮询。 - 如果设置为
false
, 在页面隐藏时会暂时停止轮询,页面重新显示时继续上次轮询。
- 在页面隐藏时,是否继续轮询。默认为
refreshOnWindowFocus: boolean
- 在屏幕重新获取焦点或重新显示时,是否重新发起请求。默认为
false
,即不会重新发起请求。 - 如果设置为
true
,在屏幕重新聚焦或重新显示时,会重新发起请求。
- 在屏幕重新获取焦点或重新显示时,是否重新发起请求。默认为
focusTimespan: number
- 屏幕重新聚焦,如果每次都重新发起请求,不是很好,我们需要有一个时间间隔,在当前时间间隔内,不会重新发起请求。默认为
5000ms
- 需要配置
refreshOnWindowFocus
使用。
- 屏幕重新聚焦,如果每次都重新发起请求,不是很好,我们需要有一个时间间隔,在当前时间间隔内,不会重新发起请求。默认为
debounceInterval: number
- 防抖间隔, 单位为毫秒,设置后,请求进入防抖模式。
throttleInterval:number
- 节流间隔, 单位为毫秒,设置后,请求进入节流模式。
扩展用法
基于基础的 useAPI,我们可以进一步封装,实现更高级的定制需求。当前 useAPI 内置了 集成请求库
,分页
和 加载更多
三种场景。你可以参考代码,实现自己的封装。参考 useRequest、usePaginated、useLoadMore 的实现。
集成请求库
如果 service 是 string
、 object
、 (...args)=> string|object
, 我们会自动使用 um-request 来发送网络请求。umi-request 是类似 axios、fetch 的请求库。
// 用法 1
const { data, error, loading } = useAPI('/api/userInfo');
// 用法 2
const { data, error, loading } = useAPI({
url: '/api/changeUsername',
method: 'post',
});
// 用法 3
const { data, error, loading } = useAPI((userId)=> `/api/userInfo/${userId}`);
// 用法4
const { loading, run } = useAPI((username) => ({
url: '/api/changeUsername',
method: 'post',
data: { username },
}), {
manual: true,
});
Q:如果我要使用 axios
、fetch
咋办?如何设置 umi-request
的全局配置?
A:你可以通过设置 requestMehod
即可。参考 示例代码。当然,你可以通过 UseAPIProvider 全局设置请求方法哦。
API
const {...} = useAPI<R>(
service: string | object | ((...args:any) => string | object),
{
...,
requestMehod?: (service) => Promise
})
service
如果 service 是 string
、 object
、 (...args)=> string|object
,则自动使用 umi-request
来发送请求。
参数
requestMehod:(service: string|object)) => Promise
- 异步请求方法,参数为 service 或 service 返回的参数。如果设置该参数,则默认使用该函数发送网络请求。
分页
通过设置 options.paginated = true
, useAPI 将以分页模式运行,此时会有以下特性:
- useAPI 会自动管理分页
current
,pageSize
。service 的第一个参数为{current, pageSize}
。 - service 返回的数据结构必须为
{list: Item[], total: number}
,如果不满足,可以通过options.formatResult
转换一次。 - 会额外返回
pagination
字段,包含所有分页信息,及操作分页的函数。 refreshDeps
变化,会重置current
到第一页,并重新发起请求,一般你可以把 pagination 依赖的条件放这里。
示例 1
普通的分页场景,我们会自动管理 current
和 pageSize
const { data, loading, pagination } = useAPI(
({ current, pageSize }) => getUserList({ current, pageSize }),
{
paginated: true,
}
);
示例 2
由于 antd Table 使用比较广泛,我们特别支持了 antd Table 需要的分页格式,及 sorter
、 filters
等。你可以通过 result.tableProps
, result.filters
, result.sorter
访问到这些属性。
const { tableProps, sorter, filters } = useAPI((params) => {
return getUserList(params);
}, {
paginated: true
});
return (<Table columns={columns} rowKey="id" {...tableProps} />);
示例 3
在 cacheKey
场景下, run
的参数 params
是可以缓存的,利用这个特点,我们可以实现 pagination 相关条件的缓存。
一个复杂的带条件,带缓存的 pagination 例子。
API
const {
...,
pagination: {
current: number;
pageSize: number;
total: number;
totalPage: number;
onChange: (current: number, pageSize: number) => void;
changeCurrent: (current: number) => void;
changePageSize: (pageSize: number) => void;
};
tableProps: {
dataSource: Item[];
loading: boolean;
onChange: (
pagination: PaginationConfig,
filters?: Record<keyof Item, string[]>,
sorter?: SorterResult<Item>,
) => void;
pagination: {
current: number;
pageSize: number;
total: number;
};
};
sorter?: SorterResult<Item>;
filters?: Record<keyof Item, string[]>;
} = useAPI(service, {
...,
paginated,
defaultPageSize,
refreshDeps,
});
返回
pagination
- 分页数据及操作分页的方法。
tableProps
- 适配 antd Table 组件的数据结构,可以直接用在 Table 组件上。
sorter
- antd Table sorter。
filters
- antd Table filters。
参数
paginated: boolean
- 是否开启分页模式,默认为
false
。 - 如果设置为
true
,则开启分页模式。在分页模式下,service 的第一个参数为{curret, pageSize, sorter, filters}
。 - 响应结果或
formatResult
结果必须为{list: Item[], total: number}
。
- 是否开启分页模式,默认为
refreshDeps
- 分页模式下,
refreshDeps
变化,会重置current
到第一页,并重新发起请求,一般你可以把依赖的条件放这里。
- 分页模式下,
加载更多
通过设置 options.loadMore = true
, useAPI 将以 loadMore 模式运行,此时会有以下特性:
- service 返回的数据结构必须包含
{list: Item[], nextId: string|undefined}
,如果不满足,可以通过options.formatResult
转换一次。 - useAPI 会自动管理列表数据 。service 的第一个参数为
nextId
。 - 会额外返回
result.loadingMore
和result.loadMore
。 refreshDeps
变化,会清空当前数据,并重新发起请求,一般你可以把 loadMore 依赖的条件放这里。
const { data, run, loadMore, loading, loadingMore } = useAPI((nextId) => {
return getUserList(nextId);
}, {
loadMore: true
});
API
const {
...,
loadMore,
loadingMore,
} = useAPI(service, {
...,
loadMore,
refreshDeps,
});
返回
loadMore: ()=>void
- 触发加载更多
loadingMore: boolean
- 是否正在加载更多
参数
loadMore: boolean
- 是否开启加载更多模式,默认为
false
。 - 如果设置为
true
,则开启加载更多模式。在该模式下,service 的第一个参数为nextId
。 - 响应结果或
formatResult
结果必须为{list: Item[], nextId: string|undefined}
。
- 是否开启加载更多模式,默认为
refreshDeps
- 加载更多模式下,
refreshDeps
变化,会清空当前数据,并重新发起请求,一般你可以把依赖的条件放这里。
- 加载更多模式下,
全局配置
UseAPIProvider
你可以通过 UseAPIProvider
在项目的最外层设置全局 options。
import {UseAPIProvider} from '@umijs/use-api';
export function ({children})=>{
return (
<UseAPIProvider value={{
refreshOnWindowFocus: true,
requestMethod: (param)=> axios(param),
...
}}>
{children}
</UseAPIProvider>
)
}