`friday-async` 抛弃了redux数据流管理方式,全面拥抱hooks生态, 同时还支持纯函数的async能力,简化应用复杂度之下,还大大提升研发效率。
npm install friday-asyncfriday-async 抛弃了redux数据流管理方式,全面拥抱hooks生态, 同时还支持纯函数的async能力,简化应用复杂度之下,还大大提升研发效率。
``bash
$ npm install friday-async --save
or
$ yarn add friday-async --save
`friday-async可以脱离friday框架单独使用
在以往redux库下的react应用,redux所承担的可能是一个全局状态管理,一个单向数据流,但是在某些应用下,我们未必需要管理大量的全局状态和需要一个管理状态和视图的工具。
很多时候我们使用redux只需要它的异步请求。在react hooks到来之后,我们尝试放弃单向数据流,因为它带来的更多大量的重复工作和样板代码,所以我们借鉴swr的思想来封装一个请求器。让状态和视图管理更加简单,耦合更松散。
friday-async 提供 createGetApi | createPostApi 来生成一个api配置
createGetApi | createPostApi 生成的api可以同时给useRequest | dispatchAsync使用,做到一次生成,随地使用,同时能自动推导输入输出的类型定义,开发重构有更多的保障
`javascript
import { createGetApi, useRequest, dispatchAsync } from 'friday-async'
interface RequestParams {
id: number
}
interface ResponseData {
id: number
name: string
}
const getUserInfo = createGetApi
url: '/userInfo'
})
// 应用在react hook中
const APP = () => {
// 当参数改变,useRequest会自动监测并重新fetch
const { dataArray } = useRequest(getUserInfo({ id: 123 }))
return (
Service 概念
friday-async提供了createGetApi 、 createPostApi两个api方便快速生成api模版, 以下文档我们统一将
createGetApi 、 createPostApi称为service,我们对service的定义做一个共识:`js
export const createGetApi = (
apiConfig: ApiConfig
): HeadService => {
return (headParams: Params, otherSet = {}): LastService => (lastParams = {} as Params) => {
const nextParams = { ...headParams, ...lastParams }
return {
...apiConfig,
params: nextParams,
method: 'get',
...otherSet
}
}
} `
可以看到,service是一个柯里化函数,在进行首次配置后, 将返回一个HeadService。HeadService接收两个参数,第一个参数,将成为service的请求参数,第二个函数otherSet作为扩展参数,方便扩展。HeadService执行之后将会返回一个函数,我们称为LastService,同时LastService也能接收一个参数,并能和HeadService的参数进行合并。通过对
service的柯里化,后续在useRequest进行定制化的时候,我们可以随意选择在HeadService或者LastService阶段传入一些定制参数。fetcher
上面我们讲到
Service, 而Service只是一个配置器而已,我们需要一个http去协助Service发起请求。friday-async提供了两个方法进行全局配置。-
AsyncRequestProvider 普通react应用`js darkimport { httpAxios, AsyncRequestProvider } from 'friday-async'
const axiosInstance = httpAxios({
baseURL: 'http://10.2.32.178:3000/mock/40/friday',
})
`
- request_middleware friday应用可以通过中间件的方法配置。`js darkimport { httpAxios, request_middleware } from 'friday-async'
export const axiosInstance = httpAxios({
baseURL: publicUrl.baseUrl,
})
const axios_middleware = request_middleware({axiosInstance})
`fetcher只是作为一个请求器,而我们对请求的结果,请求的场景常常需要一些特殊的制定,比如在更新参数的时候自动触发重新请求,页面获取焦点后重新请求,等等。useRequest 和 dispatchAsync则为此而生。API
createGetApi/createPostApicreateGetApi和createPostApi相同,都需要传入axiosconfig生成一个api,返回一个HeadService。Params和Data分别为该api的参数和返回值类型,在dispatchAsync和useRequest会自动推导``js dark`
// 生成一个get api
const getUserInfo = createGetApi<{id: number}, {id: number, name: string}>({url: '/userInfo'})
// post api
const deleteUser = createPostApi<{id: number}, {id: number, name: string}>({url: '/delete/user'})
useRequest接收HeadService或者LastService。
只有在手动模式下(config.manual == true ),useRequest才接收一个HeadService。
除此之外全部接收LastService, 在非手动下useRequest将会对LastService进行依赖检查,当LastService改变时,useRequest会重新进行请求。
不同的config将会有不同的返回,在typescirpt应用下,useRequest会自动推导出不同的返回值。
`js dark`
interface ConfigInterface = fetcherFn> {
// 错误重试时间间隔
errorRetryInterval?: number;
// 次数
errorRetryCount?: number;
// 超时
loadingTimeout?: number;
// 获取焦点触发时间间隔
focusThrottleInterval?: number;
// 删除缓存数据
dedupingInterval?: number;
// 更新缓存数据
refreshInterval?: number;
// 是否更新缓存fetcher
refreshWhenHidden?: boolean;
// 离线更新
refreshWhenOffline?: boolean;
// 页面激活重新拉数据
revalidateOnFocus?: boolean;
// 页面激活重新拉数据
revalidateOnMount?: boolean;
revalidateOnReconnect?: boolean;
// 错误重试
shouldRetryOnError?: boolean;
// 自定义请求器,已提供全局中间件
fetcher?: Fn;
// suspense
suspense?: boolean;
// 默认数据,无需使用
initialData?: Data;
// 网络是否在线
isOnline?: () => boolean;
// 窗口是否激活
isDocumentVisible?: () => boolean;
// 慢请求
onLoadingSlow?: (key: string, config: ConfigInterface) => void;
// 请求成功callback
onSuccess?: (data: Data, key: string, config: ConfigInterface) => void;
// 请求失败callback
onError?: (err: Error, key: string, config: ConfigInterface) => void;
onErrorRetry?: (err: Error, key: string, config: ConfigInterface, revalidate: revalidateType, revalidateOpts: RevalidateOptionInterface) => void;
compare?: (a: Data | undefined, b: Data | undefined) => boolean;
// 分页请求,多两个参数
defaultPageSize?: number
// 是否使用分页,
paginated?: boolean
// 下拉加载更多,和paginated 自选一个,默认分页
loadMore?: boolean
// 手动触发, 开启手动触发后,useRequest不会自动请求,需要通过run进行调用
manual?: boolean
}
以上是config完整的配置表,下面我们对关键配置进行讲解.`js dark
// default config : BaseResult
config.paginated === true // PaginationResult
config.loadMore === true // LoadMoreResult
config.manual === true // ManualResult
`paginated、loadMore、manual配置互斥,代表某一种请求场景,下面我们会用示例来展示不同特性。
在特殊场景上(如非react组件中)我们需要dispatchAsync来更简单的完成一些异步操作。
dispatchAsync返回一个promise。
`js dark
import { createGetApi, useRequest } from 'friday-async'
const getUserInfo = createGetApi<{id: number}, {id: number, name: string}>({url: '/userInfo'})
const response = useRequest(getUserInfo(params))
`useRequest
此时返回默认的返回值BaseResult
`js dark
export declare type responseInterface = {
data?: Data;
error?: Error;
revalidate: () => Promise
mutate: (data?: Data | Promise | mutateCallback, shouldRevalidate?: boolean) => Promise;
isValidating: boolean;
};
export interface BaseResult
params: Params | undefined
dataArray: Data[]
dataJson: Data
responseBlob: any
responseArray: Response
responseJson: Response
}
`
`js dark
import { createGetApi, useRequest } from 'friday-async'
import { Table } from 'Antd'
const getList = createGetApi<{id: number}, {id: number, name: string}[]>({url: '/list'})
const { pagination, tableProps } = useRequest(getList(params), {
paginated: true
})
我们可以看到,config设置了 paginated, 则返回值为PaginationResult`js dark
export interface PaginationResult extends BaseResult {
params: PaginationParams
pagination: PaginationConfig
tableProps: {
pagination: PaginationConfig
loading: boolean
onChange: (pagination: PaginationConfig) => void;
dataSource: Data[]
[key: string]: any;
}
noMore?: boolean;
loadMore: () => any
dataArray: Data[]
dataJson: Data
responseArray: PaginationResponse
responseJson: PaginationResponse
}
`#### 发起一个useRequest加载更多请求,加载更多一般适用于滚动数据展示
`js dark
import { createGetApi, useRequest } from 'friday-async'import { Button } from 'Antd'
const getList = createGetApi<{id: number}, {id: number, name: string}[]>({url: '/list'})
const App = () => {
const { list, onLoadMore } = useRequest(getList(params), {
loadMore: true
})
return (
{list}
)
}
`
config设置了 loadMore之后,返回值为LoadMoreResult:`js dark
export interface LoadMoreResult extends BaseResult {
params: PaginationParams
noMore?: boolean;
onLoadMore: () => any
dataArray: Data[]
dataJson: Data
// list 数据获取的数据汇总, dataArray为当前页数的数据
list: Data[]
responseArray: PaginationResponse
responseJson: PaginationResponse
}
`#### 发起一个手动请求的useRequest
`js dark
import { createGetApi, useRequest } from 'friday-async'import { Button } from 'Antd'
const getList = createGetApi<{id: number}, {id: number, name: string}[]>({url: '/list'})
const App = () => {
const { run } = useRequest(getList, {
manual: true
})
return (
)
}
`
config设置了 manual之后,返回值为ManualResult:`js darkexport interface ManualResult extends BaseResult {
run: (params: Params) => void
}
`
⚠️: 手动模式不需要监控动态依赖,所以我们只需要传入一个HeadService,在通过run方法进行调用,run方法将为推导你需要的参数。
#### 使用
dispatchAsync发起一个请求`js dark
import { createPostApi, dispatchAsync } from 'friday-async'const deleteUser = createPostApi<{id: number}, {id: number, name: string}>({url: '/delete/user'})
const deleteController = async () => {
const responst = await dispatchAsync(deleteUser({id: 2}))
}
`Tips:
- 当Api不需要参数时,传入一个
void即可: createPostApi