likes
comments
collection
share

React-Query

作者站长头像
站长
· 阅读数 4

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中我们可以直观的看到已经缓存下来的数据和整个项目的配置,以及各个接口的状态等