解决react状态管理---React Query

React Query

什么是React Query

  • react-query是一个适用于react hooks的请求库,它可以为任何类型的异步数据提供React状态管理功能,使React中的获取、缓存、同步和更新服务器数据变得轻而易举
  • react-query与一些传统的状态管理库如redux,mobx不同,它是负责管理服务器与客户端之间的状态,一些用户交互的中间状态,如loading状态,错误信息等都是通过hooks直接返回
  • React Query官网

初步使用

  1. yarn add react-query or npm i react-query安装react-query
  2. 使用QueryClientProvider组件连接并提供一个QueryClient到你的应用程序
    import { QueryClient, QueryClientProvider } from 'react-query'
    
    <QueryClientProvider client={new QueryClient()}>
      { ... }
    </QueryClientProvider>

Devtools

  • yarn add react-query-devtools or npm i --save react-query-devtools安装Devtools
  • react-query-devtools是与react-query相匹配的开发工具
  • 可在开发中实时查看缓存,手动获取和删除查询等等
    import { ReactQueryDevtools } from 'react-query-devtools'
    
    const App = () => {
      return (
        <>    
          { ... }
          <ReactQueryDevtools initialIsOpen={true} />
        </>
      );
    };

增删改查

  • react-query最常用的两个hook,查询(useQuery)、增删改(useMutation)

useQuery

  • useQuery:在React Query中,查询是对某些异步数据源的声明性依赖。查询可以与任何基于Promise的方法(GET)一起使用,从服务器获取数据
    const useTodos = (param) => {
      const request = useHttp()
    
      /**
       * 第一个参数是QueryKey,是查询的关键,是一个独一无二的key,并在之后的增删改中需要,
       *   如果需要动态的QueryKey,可以使用数组的方式,如['todos', params]
       * 第二个参数是用于获取数据的异步函数
       * useQuery的响应返回就是获取到的数据和一些中间状态,如isLoading,error,isIdle...
       */
      return useQuery('todos', () =>
        request('todos', { data: param })
      )
    }
    
    // 在UI组件调用
    const { isLoading, error, data: todos } = useTodos()

useMutation

  • useMutation:常用于创建/更新/删除数据或执行服务器副作用
    const useAddTodo = () => {
      const request = useHttp()
    
      /**
       * 第一个参数是执行操作的异步函数,在返回的mutate中触发
       * 第二个参数是执行成功或者失败的一些配置函数,可用于一些处理缓存的操作,例如乐观更新
       */
      return useMutation(
        (data) =>
          request(`todos`, {
            data,
            method: 'POST',
          }),
        {
          onSuccess(){}
          onError(){}
          onSettled(){}
          ...
        }
      )
    }
    
    // 操作组件调用
    const TodosAddBtn = () => {
      ...
      const { mutateAsync, isLoading, error } = useAddTodo()
      return <Button onClick={() => mutateAsync(todoData)}>add</Button>
    }
例:用第二个参数配置乐观更新
  • 乐观更新就是在一些请求或者数据处理没有结束的时候,提前给用户显示理想的结果,如果失败就回滚更新
    const useAddConfig = (queryKey) => {
      // 获取当前QueryClient的实例
      const queryClient = useQueryClient()
      
      return {
        // 当mutate被调用时触发
        async onMutate(target) {
          // 获取当前数据快照,用于错误时回滚更新
          const previousItems = queryClient.getQueryData(queryKey)
          // 乐观更新为新值
          queryClient.setQueryData(queryKey, (old) => {
            return (target, old) => (old ? [...old, target] : [])
          })
    
          // 这个返回值会作为最后一个参数传递给onError和onSettled
          return { previousItems }
        },
        // 成功回调 清除缓存
        onSuccess: () => queryClient.invalidateQueries(queryKey),
        // 失败回调
        onError(error, newItem, context) {
          // 当前queryKey的数据回滚
          queryClient.setQueryData(
            queryKey,
            context.previousItems
          )
        },
        // 无论错误或者成功都会触发,此例子没有使用
        onSettled() {}
      }
    }

总结

  • 本地/客户端中间状态
    • redux与react-query都可,没有较大的优缺点
  • 服务端中间状态
    • 推荐react-query,将服务器状态从全局状态中解放出来,用更少的代码实现复杂的需求,让你的状态管理更优雅