前言
react-query 主要用于管理服务端的全局数据,与 redux 结合使用,可以区分管理客户端与服务端的状态,接下来的分享,会围绕 useQuery,useQueryClient,useMutation 等三个常用接口展开,演示缓存以及状态管理。
API
useQuery 用法如下:
useQuery<Project[]>(['peojects', parm], () => client('projects', { data: parm }))
useQuery 是reqct-query内置的hook,从函数签名上看,第一个参数是 queryKey,第二个参数是 Fn
(alias) useQuery<Project[], unknown, Project[]>(
queryKey: QueryKey,
queryFn: QueryFunction<Project[]>,
options?: UseQueryOptions<Project[], unknown, Project[]> | undefined
): UseQueryResult<...> (+2 overloads)
queryKey 一般是 string类型,用于标识该数据对应的 Key,第二个参数是回调函数,主要用于获取异步数据。从用法上看,queryKey 也支持另外一种写法,既 [ ],这是 tuple 类型,'project'表示 Key,parm 则是作为缓存数据存在 react-query 中,同时 parm 更是作为一个依赖,用于监听内存刷新。
在 useQuery 指定的内存后,可以通过 useQueryClient 来获取数据。useQueryClient 可以浅意的理解为管理中心,缓存数据的操作(增删改查)都是经由它触发,比如,获取内存数据以及刷新内存数据
const queryClient = useQueryClient()
queryClient.getQueryData(queryKey) // 内存数据
queryClient.invalidateQueries(queryKey) // 刷新内存数据
useMutation 是操作型 API,其实可以把它等同于多功能函数来看。useMutation 区分了同步逻辑和异步逻辑,直接集成了 success ,Error 的处理,查看函数签名,如下:
(alias) useMutation<any, any, Partial<Project>, { previousItems: unknown;}>
(mutationFn: MutationFunction<any, Partial<Project>>,
options?: UseMutationOptions<any, any, Partial<...>,
{ previousItems: unknown;}> | undefined):
UseMutationResult<...> (+3 overloads)
第一个参数是 Fn,是用于异步逻辑处理,第二参数是 options,根据异步处理的结果进行回调。
useMutation 格式用法如下:
useMutation(
(parm: Partial<Project>) => client(`projects/${parm.id}`, {
method: 'PATCH',
data: parm
}),
{
onSuccess: () => {}, // 成功后回调,一般在这里设置内存
async onMutate(targe: Partial<Project>) {
// 同步逻辑
return data;
},
onError(error:any,newItem:Partial<Project>,context:any) {
// 错误处理
}
}
)
范例代码
范例场景为删除一笔数据,View 层代码如下:
const { mutate:deleteProject} = useDeletProject(useProjectsQueryKey())
const confirmDeletProject = (id:Number) => {
Modal.confirm({
title:'确定删除这个项目嘛?',
content:'点击确定删除',
okText:'确定',
onOk(){
deleteProject({id})
}
})
}
return <Dropdown overlay={
<Menu>
<Menu.Item onClick={()=> confirmDeletProject(project.id)} key={'delete'}>删除</Menu.Item>
</Menu>
}>
</Dropdown>
useDeletProject 如下:
useMutation(
({id}:{id:Number}) => client(`projects/${id}`, { method: 'DELETE' }),
onSuccess: () => queryClient.invalidateQueries(queryKey),
async onMutate(targe: any) {
const previousItems = queryClient.getQueryData(queryKey)
queryClient.setQueryData(queryKey, (old?: any[]) => {
return callback(targe,old)
})
return { previousItems }
},
onError(error: any, newItem: any, context: any) {
// error下不能刷新内粗,进行回滚
queryClient.setQueryData(queryKey, context.previousItems)
}
)
使用心得
从开发者角度看,useMutation 的设计并不算友好,因为开发人员偏向"同步编码",不管是 Promise, async/await 亦或是中间件(thunk,saga)等等,都在是尽力去规避回调地狱并让代码看上去是"同步运行"。useMutation 则是直白的区分了同步代码和异步代码的位置,这要求程序员需要将业务场景切割,分配到对应的代码位置,然后在 react-query 的语境中,再次重组。
不过,react-query 面向服务端数据管理,将前端的数据管理又细化了,理念蛮新颖的,提供的缓存机制也属于前端优化,UI 体验有一个好的提升。