@umijs/use-api
TypeScript icon, indicating that this package has built-in type declarations

0.3.0 • Public • Published

useAPI

Production-ready React Hooks library for manage asynchronous data.

NPM version NPM downloads

特性

  • 自动请求/手动请求
  • SWR(stale-while-revalidate)
  • 缓存/预加载
  • 屏幕聚焦重新请求
  • 轮询
  • 防抖
  • 节流
  • 并行请求
  • loading delay
  • 分页
  • 加载更多,数据恢复 + 滚动位置恢复
  • [ ] 错误重试
  • [ ] 请求超时管理
  • [ ] suspense
  • ......

在线体验

Edit

快速开始

安装

$ 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 ,直接修改 datamutate 函数参数可以为 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'
});

1

预加载

代码示例

同一个 cacheKey 的请求,是全局共享的,也就是你可以提前加载数据。利用该特性,可以很方便的实现预加载。你可以在线上例子中体验一下。

屏幕聚焦重新请求

代码示例

如果你设置了 refreshOnWindowFocus = true ,则在浏览器窗口 refoucrevisible 时,会重新发起请求。

你可以通过设置 focusTimespan 来设置请求间隔,默认为 5000ms

const { data, loading } = useAPI(getUserInfo, {
  refreshOnWindowFocus: true,
  focusTimespan: 5000
});

并行请求

代码示例1 代码示例2

通过 fetchKey ,可以将请求进行分类,每一类的请求都有独立的状态,你可以在 fetches 中找到所有的请求。

const { run, fetches } = useAPI(disableUser, {
  manual: true,
  fetchKey: (id) => id,
});

2

3

Loading Delay

代码示例

通过设置 loadingDelay ,可以延迟 loading 变成 true 的时间,有效防止请求抖动。

const withLoadingDelayAction = useAPI(getCurrentTime, {
    loadingDelay: 200
});

4

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
  • loading: boolean

    • service 是否正在执行。
  • run: (...args: any[]) => Promise

    • 手动触发 service 执行,参数会传递给 service。
  • params: any[]

    • 当次执行的 service 的参数数组。比如你触发了 run(1, 2, 3),则 params 等于 [1, 2, 3]
  • 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 时触发,参数为 dataparams
    • 如果有 formmatResult ,则 data 为格式化后数据。
  • onError: (error: Error, params: any[]) => void

    • service 报错时触发,参数为 errorparams
  • fetchKey: (...params: any[]) => string

    • 根据 params,获取当前请求的 key,设置之后,我们会在 fetches 中同时维护不同 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 内置了 集成请求库分页加载更多 三种场景。你可以参考代码,实现自己的封装。参考 useRequestusePaginateduseLoadMore 的实现。

集成请求库

如果 service 是 stringobject(...args)=> string|object, 我们会自动使用 um-request 来发送网络请求。umi-request 是类似 axios、fetch 的请求库。

代码示例1 代码示例2

// 用法 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:如果我要使用 axiosfetch 咋办?如何设置 umi-request 的全局配置? A:你可以通过设置 requestMehod 即可。参考 示例代码。当然,你可以通过 UseAPIProvider 全局设置请求方法哦。

API
const {...} = useAPI<R>(
  service: string | object | ((...args:any) => string | object), 
  {
    ...,
    requestMehod?: (service) => Promise
  })
service

如果 service 是 stringobject(...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

代码示例

普通的分页场景,我们会自动管理 currentpageSize

const { data, loading, pagination } = useAPI(
  ({ current, pageSize }) => getUserList({ current, pageSize }),
  {
    paginated: true,
  }
);
示例 2

代码示例

由于 antd Table 使用比较广泛,我们特别支持了 antd Table 需要的分页格式,及 sorterfilters 等。你可以通过 result.tablePropsresult.filtersresult.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 例子。

5

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.loadingMoreresult.loadMore
  • refreshDeps 变化,会清空当前数据,并重新发起请求,一般你可以把 loadMore 依赖的条件放这里。
const { data, run, loadMore, loading, loadingMore } = useAPI((nextId) => {
  return getUserList(nextId);
}, {
  loadMore: true
});

6

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>
  )
}

致谢

License

MIT

Readme

Keywords

none

Package Sidebar

Install

npm i @umijs/use-api

Weekly Downloads

10

Version

0.3.0

License

ISC

Unpacked Size

126 kB

Total Files

65

Last publish

Collaborators

  • yifankakaxi
  • xierenyuan
  • stormslowly
  • xiefengnian
  • sorrycc
  • chenshuai2144
  • kuitos
  • peachscript
  • xiaohuoni