前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用React-Query解决接口请求的麻烦事

使用React-Query解决接口请求的麻烦事

作者头像
Jou
发布2023-09-16 08:52:15
5910
发布2023-09-16 08:52:15
举报
文章被收录于专栏:前端技术归纳前端技术归纳

前言

如果你平常会写前后端交互的react页面,那你一定写过这样的代码.

代码语言:javascript
复制
function App() {

  const [data, updateData] = useState(null);
  const [isError, setError] = useState(false);
  const [isLoading, setLoading] = useState(false);
  
  useEffect(async () => {
    setError(false);
    setLoading(true);
    try {
      const data = await axios.get('/api/user');
      updateData(data);
    } catch(e) {
      setError(true);
    }
    setLoading(false);
  }, [])
   
  if(isLoading) return ...
  
  if(isError) return ...
  
  return <List users={data}/>
}

这是一个组件拉取服务端数据的简单例子,在组件中,我们简单拉取了一个接口的数据,并监听接口的状态,根据状态来更新不同的UI。

如果有性能上的要求,那这里可能还需要加上一段缓存的逻辑

代码语言:javascript
复制
//...
    try {
+      if(sessionUtil.get('users'){
+          updateData(sessionUtil.get('users'));
+          return;
+      }
      const data = await axios.get('/api/user');
+     sessionUtil.set('users',data)
      updateData(data);
    } catch(e) {
      setError(true);
    }
//...

至此,这个组件已经变得相当复杂了,如果组件拉取了好几个接口,那么这一套逻辑还得写好几遍。

一些状态管理库的弊端

许多状态管理库,比如redux,可以很流畅的管理页面的状态,也有处理副作用的能力,但往往不能很好的处理服务端的状态,因为处理服务端的状态,通常还包括:

  • 缓存
  • 将对同一数据的多个请求消除为一个请求
  • 在后台更新“过期”数据
  • 知道数据何时“过期”
  • 尽快反映数据更新
  • 性能优化,如分页和延迟加载数据
  • 管理内存和服务器状态的垃圾收集
  • 使用结构共享记忆查询结果

直到React-Query的出现,上面的问题都变得迎刃而解。

React-Query

React Query 是一个开箱即用,零配置的服务端状态管理库,支持RestfulGraphQL两种类型的请求,它能帮助你很好的获取、同步、管理和缓存你的远程数据。它提供了几个简单的Hooks,借助它们可以很轻松的完成对后端数据的增删改查等操作,无需再写繁琐的数据拉取和状态判断等代码。

React-Query的官方文档没有大纲,阅读起来相当不方便,个人感觉,直接阅读github源码项目中的docs要更方便一些。链接地址:github.com/TanStack/qu…

简单使用

QueryClientProvider

首先,需要在组件外层定义一个queryClient作为组件操作和使用数据的一个共同容器,通过QueryClientProvider组件注入到项目中。

代码语言:javascript
复制
import {
  QueryClient,
  QueryClientProvider,
  useQuery,
} from '@tanstack/react-query'

const queryClient = new QueryClient()

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Example />
    </QueryClientProvider>
  )
}

在创建QueryClient的时候,我们可以传入一些参数,用于管理项目中的请求、缓存、日志的相关配置,这些配置会对整个项目生效,其中包含了四个模块的配置参数。

代码语言:javascript
复制
new QueryClient({
  queryCache?: QueryCache;
  mutationCache?: MutationCache;
  logger?: Logger;
  defaultOptions?: DefaultOptions;
})
  • queryCache: 请求缓存相关配置
  • mutationCache: 数据修改缓存相关配置
  • logger: 日志相关配置
  • defaultOptions:请求基础配置

其中defaultOptions用于配置项目中useQuery请求的管理,常用的配置如下:

  • staleTime: 重新获取数据的时间间隔 默认0
  • cacheTime: 数据缓存时间 默认 1000 60 5 5分钟
  • retry: 失败重试次数 默认 3次
  • refetchOnWindowFocus: 窗口重新获得焦点时重新获取数据 默认 false
  • refetchOnReconnect: 网络重新链接
  • refetchOnMount: 实例重新挂载时重新拉取请求
  • enabled: 如果为“false”,“useQuery”不会触发,需要使用其返回的“refetch”来触发操作
  • queryFn:全局定义请求方法,其他地方使用时只需要直接传入请求参数
useQuery

useQueryReact-Query提供的用于请求接口并管理请求状态等信息的Hook。

例如:

代码语言:javascript
复制
function Example() {
  const { isLoading, error, data } = useQuery({
    queryKey: ['repoData'],
    queryFn: () =>
      fetch('https://api.github.com/repos/TanStack/query').then(
        (res) => res.json(),
      ),
  })

  if (isLoading) return 'Loading...'

  if (error) return 'An error has occurred: ' + error.message

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.description}</p>
      <strong>👀 {data.subscribers_count}</strong>{' '}
      <strong>✨ {data.stargazers_count}</strong>{' '}
      <strong>🍴 {data.forks_count}</strong>
    </div>
  )
}

userQuery接收一个配置对象,其中

  • queryKey:必传,用作请求数据缓存的唯一key值,也可以在数组中,写入多项如:['repoData', '1'],这样React-Query在使用的时候会自动把它拼接为/repoData/1,这个在缓存用户访问过的页面时,非常有用。
  • queryFn:用于请求的方法,如果在QueryClient中配置了,这里可以不必再写,需要返回请求完成后所处理的数据。

除了这两项基本的参数,useQuery还可以传入上面defaultOptions的所有参数,来表示对这个请求单独的配置。

然后useQuery会返回一个对象,里面包含着请求相关的所有信息,这些信息会随着请求的进度而改变,就无须我们再使用一组state变量来进行管理了,常用的包括:

  • isLoading:请求是否正在进行
  • error:请求是否出错,这里只会对500404等做出反应,如果有其他情况错误情况,需要在请求方法里面throw
  • isSuccess: 请求是否成功
  • status: 请求状态,包含上面几种情况
  • data:返回数据

除此之外,使用useQuery拉取回来的数据,会被默认缓存起来,然后可以通过配置过期时间,重新拉取等策略来进行管理。

useQueryClient

通过useQueryClient,我们可以获取到之前注入的容器实例,里面保存着所有我们缓存的信息,以及配置信息,而它本质上其实也是对React.useContext的封装。

以上面Example组件为例,如果我们想在另一个组件访问这些数据。

代码语言:javascript
复制
function Example() {
  const queryClient = useQueryClient()

  const data = queryClient.getQueryData(['repoData'])

  return (
    //...
  )
}

除了访问缓存数据,queryClient还有很多API:

大家可以根据需要去官网查阅。

useMutation

除了获取数据,很多时候还需要处理数据的修改,比如说最简单的todo list例子,除了拉取数据列表,还需要增删改数据,而这个时候除了需要发送接口,还需要修改本地的数据,React-Query提供了useMutation来帮我们完成这些事情。

以上面Example组件为例,我们已经拉取到了data,现在我们想新增一条数据,那我们可以

代码语言:javascript
复制
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的构造方法中。

代码语言:javascript
复制
  const updateData = async (newData) => {
    mutate(newData);
  };

以上就是React-Query最核心的对服务端数据进行增删改查的功能,除此之外,React-Query还有很多其他的能力。

数据预获取

有时候我们不需要整个页面loading来等待数据加载,我们更希望在用户操作之前就拉取完数据,比如用户hover详情链接,而不是点击详情的时候。

那我们可以使用queryClientprefetchQuery方法,提前拉取到用户可能会访问的数据,并加入到缓存中,由于不需要监听服务端状态等,所以这个方法会比useQuery高效许多。

代码语言:javascript
复制
onMouseEnter={async () => {
  await queryClient.prefetchQuery({
    queryKey: ['character', char.id],
    queryFn: () => getCharacter(char.id),
    staleTime: 10 * 1000, // only prefetch if older than 10 seconds
  })
}}

分页缓存

通过动态设置queryKey,并将keepPreviousData设置为true,我们可以很轻松的缓存之前页码的数据

代码语言:javascript
复制
  const { status, data, error, isFetching, isPreviousData } = useQuery({
    queryKey: ['projects', page],
    queryFn: () => fetchProjects(page),
    keepPreviousData: true,
    staleTime: 5000,
  })

滚动列表渲染

使用useInfiniteQuery定义拉取数据的方法,以及上下页的逻辑,然后会返回更新页面数据的状态,以及触发更新的方法。

代码语言:javascript
复制
  const {
    status,
    data,
    error,
    isFetching,
    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配套开发工具

导入开发工具

代码语言:javascript
复制
import { ReactQueryDevtools } from 'react-query/devtools'

<ReactQueryDevtools initialIsOpen={false} />

默认情况下,当process.env.NODE ENV === 'production' 时开启 Devtools ,不必担心构建时需要排除他们

浮动模式下开启,会将devtools作为固定的浮动元素安装在开发的应用程序中,并在屏幕一角提供一个切换按钮以显示和隐藏devtools

在devtools中我们可以直观的看到已经缓存下来的数据和整个项目的配置,以及各个接口的状态等。

image.png
image.png

最后

感谢你能看到这里,本文简单介绍了React-Query对服务端数据进行增删改查的功能实现,以及React-Query的一些其他能力,希望对你有用,React-Query的使用场景没有其他状态管理库那么广泛,但还是能解决很多服务器拉取数据的痛点。

笔者之后也会继续更新React-Query的其他使用场景,如果可以的话,不妨点个赞再走呢,这对我很重要。

参考

推荐官网地址:github.com/TanStack/qu…

官方用例地址:github.com/TanStack/qu…

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一些状态管理库的弊端
  • React-Query
    • 简单使用
      • QueryClientProvider
      • useQuery
      • useQueryClient
      • useMutation
    • 数据预获取
      • 分页缓存
        • 滚动列表渲染
          • devTools配套开发工具
          • 最后
            • 参考
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档