react-query手把手教程⑥-使用QueryClient

3,152 阅读3分钟

写在前面

由于国内较少有比较系统的react-query教程,因此笔者结合官方文档以及官方课程的内容,希望写一个较为全面的教程。本文将以各种例子作为切入点,尽可能通俗易懂地讲解相关知识点。如果有错误,还请大家在评论区指出,笔者会尽快改正。

由于短篇的编写较为仓促,后期会总结一遍万字长文,重新根据短篇内容整理一篇完整教程。

目录

请各位读者回忆一下在第一章中,在index.ts文件内初始化react-query的代码:

import ReactDOM from "react-dom";
import App from "./App";
+ import { QueryClient, QueryClientProvider } from "react-query";

+ const queryClient = new QueryClient();

const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);

root.render(
+  <QueryClientProvider client={queryClient}>
    <App />
+  </QueryClientProvider>
);

在添加的代码中,除了QueryClientProvider外,最显眼的应该是QueryClient

在react-query中QueryClient在后台默默地处理着请求及数据缓存的工作。

react-query有很多默认的配置,如果你想创建一个全局配置,可以在new QueryClient()时传入。如果你希望请求的数据过期时间和缓存时间均是10s且请求遇到错误后只重试1次,你可以进行如下的配置:

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 10 * 1000,
      cacheTime: 10 * 1000,
      retry: 1,
    },
  },
})

如果你有兴趣,可以在react-query官方文档中查找到更多配置

接下来将介绍一个默认查询函数的小技巧

默认查询函数

如果查询键可以很好地描述后端接口的路径地址,那么完全可以全局默认的查询函数来减少代码量(一般适合不复杂的项目,比如自建简单博客一类的)。

下面还是以github查询仓库issue列表的api举例:

https://api.github.com/repos/${org}/${repo}/issues

可以设置下面的查询键

const queryKey = ["repos", org, repo, "issues"]

由于查询函数中可以拿到queryKey的内容,因此我们完全可以通过添加/拼接请求接口,解放双手,写一遍查询函数即可。

const queryGithub = ({queryKey}) => {
  const BASE_URL = `https://api.github.com/`
  const apiPath = queryKey.join('/')
  const requestUrl = `${BASE_URL}${apiPath}`

  return fetch(requestUrl).then(res => res.json())
}

但是还有个问题,如果需要添加查询参数,此时上面的代码就不好用了,还需要对它进行扩展:

在下面的例子中,需要引入qs库,全部代码如下:

在线查看例子

import * as React from 'react';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools';
import qs from 'qs';

import App from './App';

const queryGithub = ({ queryKey }) => {
  const BASE_URL = `https://api.github.com/`;
  const lastQueryKey = queryKey[queryKey.length - 1];

  let queryParams = typeof lastQueryKey === 'object' ? qs.stringify(lastQueryKey) : '';

  const apiPath = queryKey.join('/');
  const requestUrl = `${BASE_URL}${apiPath}${queryParams}`;

  return fetch(requestUrl, {
    headers: {
      Authorization: '',
    },
  }).then((res) => res.json());
};

const rootElement = document.getElementById('root');
const root = createRoot(rootElement);
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      queryFn: queryGithub,
    },
  },
});

root.render(
  <StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
      <ReactQueryDevtools />
    </QueryClientProvider>
  </StrictMode>
);

如果不想用默认的查询函数,你也可以传入查询函数,这样就会覆盖默认的查询参数。

在项目中灵活使用这个技巧,来减少项目中的代码量,解放双手🙌🏻

使用queryClient请求数据

在项目的组件中,也可以调用queryClient,使用useQueryClient钩子,就可以获取queryClient

import { useQueryClient } from 'react-query'
// 此处省略一堆代码
const queryClient = useQueryClient();

除了使用useQuery请求数据外,在特殊场景下(如:强制触发查询等)可以使用queryClient.fetchQuery请求数据。

请注意,该方法是异步方法,因此如果使用await需要用try/catch包裹一下。

把上一个例子中的代码稍加改造:

import * as React from 'react';
import { useQueryClient } from 'react-query';
import qs from 'qs';
import './style.css';

export default function App() {
  const queryClient = useQueryClient();

  const queryGithub = ({ queryKey }) => {
    const BASE_URL = `https://api.github.com/`;
    const lastQueryKey = queryKey[queryKey.length - 1];

    let queryParams =
      typeof lastQueryKey === 'object' ? qs.stringify(lastQueryKey) : '';

    const apiPath = queryKey.join('/');
    const requestUrl = `${BASE_URL}${apiPath}${queryParams}`;

    return fetch(requestUrl, {
      headers: {
        Authorization: '',
      },
    }).then((res) => res.json());
  };

  const handleFetchIssues = async () => {
    try {
      const data = await queryClient.fetchQuery(
        ['repos', 'facebook', 'react', 'issues'],
        queryGithub
      );

      console.log(data);
    } catch (e) {
      console.log(e);
    }
  };

  return (
    <div>
      <button onClick={handleFetchIssues}>获取数据</button>
    </div>
  );
}

在上面使用queryClient.fetchQuery请求时

  • 如果在缓存中有对应的数据(通过查询键匹配)且未过期,你可以立即得到结果,无需请求。
  • 如果没有缓存或缓存已经过期,那么react-query将会重新请求并且缓存数据。

如果是一般组件内的请求,还是建议使用useQuery,而不是queryClient.fetchQuery