React-Query
React Query 是一个开箱即用,零配置的服务端状态管理库,支持Restful和GraphQL两种类型的请求,它能帮助你很好的获取、同步、管理和缓存你的远程数据。它提供了几个简单的Hooks,借助它们可以很轻松的完成对后端数据的增删改查等操作,无需再写繁琐的数据拉取和状态判断等代码; 简单来说,react-query是一个异步状态管理的hook,内置了loading,error等状态;
reqct-query的作用
- 封装了loading,error等方便使用,减少代码量
- 多个组件请求同一个query时只发送一个请求;如下所示,List和List1都使用了getListData这个query,但是最终只会请求一次
function List() {
const { isLoading, error, data } = useQuery('getListData', () => fetchList().then(res => res.data));
if (error) {
return 'An error has occurred: ' + error.message
}
if (isLoading) {
return 'data is loading.'
}
return data.map(l => <div key={l.id}>
{l.name}
</div>);
}
function List1() {
const { isLoading, error, data } = useQuery('getListData', () => fetchList().then(res => res.data));
if (error) {
return 'An error has occurred: ' + error.message
}
if (isLoading) {
return 'data is loading.'
}
return data.map(l => <div key={l.id}>
{l.name}
</div>);
}
function App() {
const queryClient = new QueryClient();
return <div className='app'>
<QueryClientProvider client={queryClient}>
<List />
<List1/>
<ReactQueryDevtools initialIsOpen={false} position='top-right'/>
</QueryClientProvider>
</div>
- 缓存更新和时效策略
react-query的一些主要特性:
- 数据获取和缓存: react-query可以帮助你从远程服务器或本地存储获取数据,并在本地缓存这些数据。他会自动处理数据的获取、缓存和更新等操作。
- 自动数据刷新: react-query可以根据设定的条件自动刷新数据,例如定时刷新或者当页面重新聚焦时刷新等
- 无需手动管理状态: react-query会自动管理所有一步操作的状态,并提供一系列狗子函数来让您轻松地与数据交互
- 查询时效和重新获取:当数据过期或失效时, react-query会自动触发重新获取惭怍,确保得到最新的数据
- 错误处理: react-query提供了方便的错误处理机制,可以很容易地处理请求失败或其他错误情况
- 多种查询功能:除了基本的查询外, react-query还支持带有参数,依赖关系和无限滚动等高级查询功能。
react-query和redux的区别
react-query和redux都能进行状态管理和异步数据的处理,react-query更偏向于服务器之间的异步数据的管理
简单使用
QueryClientProvider
首先,需要再组件外层定义一个queryClient作为组件操作和使用数据得一个共同容器,通过QueryClientProvider组件注入到项目中。
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Example />
</QueryClientProvider>
)
}
在创建QueryClient的时候,我们可以传入一些参数,用于管理项目中的请求,缓存,日志的相关配置,这些配置会对整个项目生效,其中包含了四个模块的配置参数
new QueryClient({
queryCache?: QueryCache;//请求缓存相关配置
mutationCache?: MutationCache;//数据修改缓存相关配置
logger?: Logger;//日志相关配置
defaultOptions?: DefaultOptions;//请求基础配置
})
其中,defaultOptions用于配置项目中useQuery请求的管理,常用的配置如下:
- staleTiem:重新获取数据的时间间隔,默认为0
- cacheTime:数据缓存时间,默认1000, 60 5 5分钟
- retry:失败重试次数,默认3次;
- refetchOnWindowFocus:窗口重新获得焦点时重新获取数据,默认为false
- refetechOnReconnect:网络重新链接
- refetchOnMount:实例重新挂载时重新拉取请求
- enabled:如果为false,useQuery不会触发,需要使用其返回的refetch来触发操作
- queryFn:全局定义请求方法,其他地方使用时只需要直接传入请求参数
useQuery
useQuery是react-Query提供的用于请求接口并管理请求状态等信息的Hook
export const useGetAttachments = (subjectId: string) => {
const { data, ...rest } = useQuery({
queryKey: ['querySubjectAttachments'],
queryFn: () => {
return queryAllAttachments(subjectId).then((data: ResBasic<IAttachmentRes>) => {
return data;
});
},
});
return {
dataSourceList: data?.data?.files?.map((item: IAttachmentItem) => {
return {
title: item.file_name,
size: item.file_size,
fileType: item.type,
fileId: item.file_uuid,
downloadUrl: item.download_url,
};
}),
...rest,
};
};
useQuery接收一个配置对象,其中:
queryKey
:必传,用于请求数据缓存唯一的key值,也可以在数组中,写入多项如:['querySubjectAttachments', subjectId]这样React-Query
在使用的时候会自动把它拼接为/querySubjectAttachments/subjectId
,这个在缓存用户访问过的页面时,非常有用。- queryFn:用于请求的方法,如果在QueryClient中配置了,这里可以不必再写,需要返回请求完成后所处理的数据;
- 除了上述两项基本的参数,useQuery还可以传入上面defaultOptions的所有参数,来表示对这个单独的配置。
- useQuery会返回一个对象,里面包含着请求相关的所有信息,这些信息会随着请求的进度而改变,就无需我们再使用一组state变量来进行管理了,常用的包括:
- isLoading:请求是否正在进行;
- error:请求是否出错,这里只会对500,404等做出反应,如果有其他情况,需要再请求方法里面throw
- isSuccess:请求是否成功
- status:请求状态,包含上面几种情况
- data:返回数据 除此之外,useQuery拉取回来的数据,会被默认缓存起来,然后可以通过配置过期时间,重新拉取等
useQueryClient
通过useQueryClient
,我们可以获取到之前注入的容器实例,里面保存着所有我们缓存的信息,以及配置信息,而它本质上其实也是对React.useContext
的封装。
function Example() {
const queryClient = useQueryClient()
const data = queryClient.getQueryData(['querySubjectAttachments'])
return (
//...
)
}
除了访问缓存数据,queryClient还有很多API:
- queryClient.queryKeyCache:这个属性包含了所有当前已经缓存的查询键和相应的查询结果;
- queryClient.fetchQuery
- queryClient.fetchInfiniteQuery
- queryClient.prefetchQuery(queryKey,queryFn,options?):数据预获取,并将结果存储在缓存中,以便稍后使用
- queryClient.prefetchInfiniteQuery
- queryClient.getQueryData(queryKey):根据指定的查询键从缓存中获取数据;
- queryClient.setQueryData(queryKey,data,options?):将指定数据存储到缓存中,并触发相应更新;
- queryClient.invalidateQueries(queryKey,options?):使特定查询失效,下次访问时会重新获取最新数据;
- queryClient.invalidateQueries((key)=>key.startsWith('examplae)):通过函数形式批量使多个查询失效
- queryClient.refetchQueries(queryKeyOrPredicateFn,options?):手动触发重新获取指定或匹配条件的查询数据。
- queryClient.cancelQueries(queryKey,options?):取消正在进行中的查询请求,避免不必要的网络请求
- queryClient.removeQueries
- queryClient.resetQueries
- queryClient.clear():清除整个queryClient内容,包括所有的queries和mutations数据 …
useMutation
除了获取数据,很多时候还需要处理数据的修改,比如说最简单的todolist例子,除了拉取数据列表,还需要增删改数据,而这个时候除了需要发送接口,还需要修改本地的数据,React-Query提供了useMutation来帮我们完成这些事情。 比如,我们已经拉取了data,现在我们想新增一条数据,那我们可以:
const {isLoading,isError,isSuccess,mutate} = useMutation({
mutationFn: async (newData) => insertNewData(newData),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['repoData'] });
},
onError: (error) => {
console.log(error)
}
});
这里传入了:
- mutationFn:代表元数据的方法
- onSuccess:接口调用成功后的毁掉
- onError:失败的毁掉 返回的数据和useQuery基本相同,这里的mutate则是触发更改的方法,如果我们想执行useMutation中传入的方法,我们只需要调用mutate即可,传给mutate的参数都会被逮到useMutation的构造方法中。
const updateDate = async (newData)=>{
mutate(newData);
}
React-Query除了以上的最核心的对服务端数据进行增删改查的功能,除此之外,React-Query还有很多其他的能力。
数据预获取
有时候我们不需要整个页面loading来等待数据加载,我们更希望在用户操作之前就完成数据拉取,比如hover详情链接,而不是点击详情的时候;则可以使用queryClient的prefetchQuery方法,提前拉取到用户可能会访问的数据,并加入缓存中,由于不需要监听服务端状态,所以这个方法回避useQuery高效许多。
onMouseEnter={async ()=>{
await queryClient.prefetchQuery({
queryKey: ['character',char.id],
queryFn:()=>getCharacter(char.id),
staleTime: 10*1000,//只有超过10s之后才开始重新获取
})
}}
分页缓存
通过动态设置queryKey,并将keepPreviousDate
设置为true,我们可以很轻松的缓存之前页码的数据
const {status, data,error,isFetching,isPreviousData} = useQuery({
queryKey:['projects',page],
queryFn:()=>fetchProjects(page),
keepPreviousData:true,
staleTime: 5000
})
滚动列表渲染
使用useInfiniteQuery
定义拉取获取数据的方法,以及上下页的逻辑,然后返回更新页面数据的状态,以及触发更新的方法;
const {status,data,error,isFetchingNextPage,isFetchingPreviousPage,fetchNextPage,fetchPreviousPage,hasNextPage,hasPreviousPage} useInfiniteQuery(['projects'],async ({pageParam = 0})=>{
const res = await axios.get('/api/projects?cursor='+pageParam)
return res.data
},{
getPreviousPageParam: ( firstPage ) => firstPage.previousId ?? undefined,
getNextPageParam: ( lastPage ) => lastPage.nextId ?? undefined
})
devTools配套开发工具
导入开发工具
import {ReactQueryDevtools} from 'react-query/devtools'
<ReactQueryDevtools initialOpen={false}>
默认情况下,当process.env.NODE_ENV==='production
时开启Devtools,不必担心构建时需要排除他们;
浮动模式下开启,会将devtools作为固定的浮动元素安装在开发的应用程序中,并在屏幕一角提供一个切换按钮以显示和隐藏devtools,在devtools中我们可以直观的看到已经缓存下来的数据和整个项目的配置,以及各个接口的状态等
转载自:https://juejin.cn/post/7356447891823935497