Getting started with react-query
write in front
Since there are few more systematic react-query tutorials in China, the author hopes to write a more comprehensive tutorial based on the official documents and the content of the official courses. This article will use various examples as entry points to explain relevant knowledge points as easily as possible. If there are any mistakes, please point them out in the comment area, and the author will correct them as soon as possible.
Table of contents
- Getting started with react-query has been updated on 2022-06-04
- In -depth query keys and query functions have been updated on 2022-06-08
- Parallel requests and dependent requests have been updated on 2022-06-19
- Query result cache status and debugging tools have been updated on 2022-06-23
Imprint
This tutorial is based on the react-query@4
version, which is currently (2022-06-05) an alpha version.
Online demo based on stackblitz
platform
Start with requesting data from the backend in react
In daily development, it is inevitable to request the back-end interface. When requesting an interface, the following processing is often involved
- loading state
- Backend returns data store
- If there is an error message on the interface, display the error message
- refresh data
and many more
Let's look at an example that satisfies the above processing
Click me to see the example ①Online demo
PS: The following code will request a Zen-like (high-level) sentence from github and display it on the page
Example①👇🏻
import * as React from 'react';
export default function App() {
// 存储 后端返回数据
const [zen, setZen] = React.useState('');
// 存储 加载状态
const [isLoading, setIsLoading] = React.useState(false);
// 存储 是否请求成功
const [isError, setIsError] = React.useState(false);
// 存储 后端返回的错误数据
const [errorMessage, setErrorMessage] = React.useState('');
const fetchData = () => {
// 开始获取数据,将isLoading置为true
setIsLoading(true);
fetch('https://api.github.com/zen')
.then(async (response) => {
// 如果请求返回status不为200 则抛出后端错误
if (response.status !== 200) {
const { message } = await response.json();
throw new Error(message);
}
return response.text();
})
.then((text: string) => {
// 请求完成将isLoading置为false
setIsLoading(false);
// 接口请求成功,将isError置为false
setIsError(false);
// 存储后端返回的数据
setZen(text);
})
.catch((error) => {
// 请求完成将isLoading置为false
setIsLoading(false);
// 接口请求错误,将isError置为true
setIsError(true);
// 存储后端返回的错误数据
setErrorMessage(error.message);
});
};
React.useEffect(() => {
// 初始化请求数据
fetchData();
}, []);
return (
<div>
<h1>Zen from Github</h1>
<p>{isLoading ? '加载中...' : isError ? errorMessage : zen}</p>
{!isLoading && (
<button onClick={fetchData}>{isError ? '重试' : '刷新'}</button>
)}
</div>
);
}
In the above example
- Use
isLoading
to store加载状态
- Use
isError
to store接口是否有错误
- Use
errorMessage
to store后端返回的报错信息
- Use
zen
to store后端返回数据存储
- Recall the
fetchData
method to refresh the data
This example is just a request for an interface. If it is a real project, there will definitely be more than one request, so we will write a lot of repetitive code to meet the business needs (inner os: In fact, too much code is written to affect efficiency, so we can't get off work early. (¬_¬), and it is expensive to maintain)_
At this point, the introduction of react-query can reduce the code related to the request interface. The above example is rewritten using react-query as follows:
Click me to see the example ②Online demo
Example ②👇🏻
import * as React from 'react';
import { useQuery } from 'react-query';
const fetchData = () => {
return fetch('https://api.github.com/zen').then(async (response) => {
// 如果请求返回status不为200 则抛出后端错误
if (response.status !== 200) {
const { message } = await response.json();
throw new Error(message);
}
return response.text();
});
};
export default function App() {
const zenQuery = useQuery(['zen'], fetchData); // ①
return (
<div>
<h1>Zen from Github</h1>
<p>
{zenQuery.isLoading || zenQuery.isFetching
? '加载中...'
: zenQuery.isError
? zenQuery.error?.message
: data}
</p>
{!zenQuery.isLoading && !zenQuery.isFetching && (
<button
onClick={() => {
zenQuery.refetch();
}}
>
{zenQuery.isError ? '重试' : '刷新'}
</button>
)}
</div>
);
}
In contrast, after the introduction of react-query, the amount of code visible to the naked eye is decreasing! !
There is no need to write useState to manage the additional state caused by the request interface (if you use state management libraries such as react-redux, mobx, etc., you will also encounter similar problems), and you also do not need to initialize in useEffect(() => {}, [])
Call the interface, and react-query will handle it for us.
In the above example, you may not know much about the incoming parameters when introducing the useQuery
hook in code ①. Next, we will introduce these incoming parameters.
Query Keys and Query Functions
What are Query Keys and Query Functions?
In the process of daily development, when requesting back-end data:
- A function will be written first to request the data of the back-end interface (such as the
fetchData
function in the above example ①) - Then specify a variable (such as
zen
in example ① above) to store the data returned by the relevant backend. The variables of each interface will have different names for different interfaces to identify different data.
So how to distinguish different data obtained by different interfaces in react-query?
Going back to example ②, we use the useQuery
hook to get back-end data. The code is as follows:
const zenQuery = useQuery(['zen'], fetchData);
- Wherein
['zen']
is the query key of react-query, react-query identifies (maps) the data returned by different interfaces (or requests with different parameters of the same interface) through different query keys. Inreact-query@4
the query key must be an array. - And
fetchData
is the function that we request the back-end interface, that is, the query function.
PS: The elements inside the query key can be nested arrays, objects, strings, numbers
For example:
['zen', { form: 'confucius' }]
or['zen', ['confucius', 'Lao Tzu']]
In order to facilitate memory, for example, you can regard the query key as the key when you store localStorage
, and the value is to store the various state data we need after querying the data through the query function enter value
PS: Of course, the actual processing process and stored information will be very complicated, but the ideas are basically the same.
Some tips for writing query keys
After explaining the query key and query function, the author hopes that everyone will consider a question. This interface is relatively simple, so we can use zen
as the query key. If I have a complex interface, how should I change it? What about good design query keys?
Taking the github interface as an example, if you want to get the issue list of a warehouse in github, you can call the interface like this
https://api.github.com/repos/{owner}/{repo}/issues
Specifically, if you want to get the issue list of the react repository, you can call the following interface, you can open it in the browser and try it:
https://api.github.com/repos/facebook/react/issues
At this point, you can get the issue list in the react repository through the request interface.
Take this interface for getting the warehouse issue list as an example, you can write the query key example like this ③👇🏻
['issues', owner, repo]
In this query key we follow a principle: from general to special
First of all, the data type we get is issue, we need to put a string at the beginning of the array to identify the data type, so we set the first parameter to issues
. There are many repositories in github. These repositories usually use the user as the first-level identification, and the warehouse name is the second-level identification, as shown in the following figure
So the second and third arguments are owner
and repo
in that order.
In the above example, the reason we did not use ['issues', 'facebook', 'react']
but used ['issues', owner, repo]
is to introduce in react-query, when a variable is used as the element of the query key, when the value of the variable changes After that, react-query will call the fetchData
method again to obtain new data and cache it in the cache with the corresponding variable value as key.
That is, when the following changes occur, react-query will call the fetchData
method again, and cache the data obtained from the backend in the value corresponding to the query key ['issues', 'vuejs', 'vue']
, Similarly, when we initialize the calling interface, the obtained data is cached in the corresponding value of the query key ['issues', 'facebook', 'react']
:
['issues', 'facebook', 'react'] -> ['issues', 'vuejs', 'vue'] // 从查询react仓库的issue,变更为查询vue仓库的issue
The following example will get the latest issue in the react repository, you can view the online demo of example ④
Change the input box in the example: facebook to vuejs, react to vue, click the [View latest issue information] button, you can see the latest issue information of the vue warehouse (for the relevant data cache, you can think about the above example we said)
Click me to see the example ④Online demo
Example ④👇🏻
import * as React from 'react';
import { useQuery } from 'react-query';
const fetchData = ({ queryKey }) => {
const [, owner, repo] = queryKey;
return fetch(`https://api.github.com/repos/${owner}/${repo}/issues`, {
headers: {
Authorization: '',
},
}).then(async (response) => {
// 如果请求返回status不为200 则抛出后端错误
if (response.status !== 200) {
const { message } = await response.json();
throw new Error(message);
}
return response.json();
});
};
export default function App() {
const [inputOwner, setInputOwner] = React.useState('facebook');
const [inputRepo, setInputRepo] = React.useState('react');
const [queryKey, setQueryKey] = React.useState([inputOwner, inputRepo]);
const issueQuery = useQuery(['issues', ...queryKey], fetchData);
return (
<div>
<span>仓库:</span>
<input
name={'owner'}
value={inputOwner}
onChange={(e) => setInputOwner(e.target.value)}
/>
/
<input
name={'repo'}
value={inputRepo}
onChange={(e) => setInputRepo(e.target.value)}
/>
<button
onClick={() => {
setQueryKey([inputOwner, inputRepo]);
}}
>
查看最新issue信息
</button>
<div>
<h1>
仓库{queryKey[0]}/{queryKey[1]}最新一条issue信息
</h1>
<p>
{issueQuery.isLoading
? '加载中...'
: issueQuery.isError
? issueQuery.message
: JSON.stringify(issueQuery.data[0])}
</p>
</div>
</div>
);
}
In this example, when the value of the query key variable changes, react-query will automatically request the corresponding data after the change, and in the parameters passed in by the query function, we can also get the value of the query key when calling the query function .
You can view the cache information of react-query in DevTool to help you understand:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。