# TanStack Query (FKA React Query)
:::info
:bulb: TanStack Query (FKA React Query) is often described as the missing data-fetching library for web applications, but in more technical terms, it makes fetching, caching, synchronizing and updating server state in your web applications a breeze.
React Query 通常被描述为 React 缺少的数据获取(data-fetching)库,但是从更广泛的角度来看,它使 React 程序中的获取,缓存,同步和更新服务器状态变得轻而易举。[^TanStack]
:::
```jsx
// Before:
function Example() {
const [fetchData, setFetchData] = useState()
useEffect(() => {
fetch("https://api.github.com/repos/tannerlinsley/react-query").then(
(res) => setFetchData(res.json()),
),
}, [])
const { isLoading, error, data } = fetchData
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>
);
}
```
```jsx
// After:
import {
QueryClient,
QueryClientProvider,
useQuery,
} from "@tanstack/react-query";
const queryClient = new QueryClient();
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Example />
</QueryClientProvider>
);
}
function Example() {
const { isLoading, error, data } = useQuery(["repoData"], () =>
fetch("https://api.github.com/repos/tannerlinsley/react-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>
);
}
```
## :eight_spoked_asterisk: Motivation
:::warning
Most core web frameworks do not come with an opinionated way of fetching or updating data in a holistic way.
大多数的 Web 框架(如 React)都没有内置的从组件中获取或更新数据的方式
:::
```jsx
useEffect(() => {
fetchApi
}, [])
```
:::warning
This usually means cobbling together component-based state and side-effects, or using more general purpose state management libraries to store and provide asynchronous data throughout their apps.
这通常意味着开发者总是倾向于使用 React Hooks 将基于组件的 state 和 effects 整合在一起,或者使用更通用的状态管理库来存储和提供整个程序中的异步数据。
:::
```jsx=
const [fetchApiData, setFetchApiData] = useState()
useEffect(() => {
fetch("https://api.github.com/repos/tannerlinsley/react-query").then(
(res) => setFetchData(res.json()),
),
}, [])
```
:::warning
While most traditional state management libraries are great for working with "client state", they are not so great at working with async or server state. This is because "server state" is totally different. For starters, server state:
- Is persisted remotely in a location you do not control or own
- Requires asynchronous APIs for fetching and updating
- Implies shared ownership and can be changed by other people without your knowledge
- Can potentially become "out of date" in your applications if you're not careful
尽管大多数传统状态管理库非常适合用于处理客户端状态,但是它们并不适合处理异步或服务器状态。 这是因为服务器状态完全不同:
- 远程保留在你无法控制或拥有的位置
- 需要异步 API 进行获取和更新
- 意味着共享所有权,可以在你不知情的情况下被其他人改变
- 如果不小心,数据可能会在应用中变得"过时"
:::
```jsx
useEffect(() => {
fetch("https://api.github.com/repos/tannerlinsley/react-query").then(
(res) => setFetchData(res.json()),
),
}, [param])
```
:::warning
Once you grasp the nature of server state in your application, even more challenges will arise as you go, for example:
- Caching... (possibly the hardest thing to do in programming)
- Deduping multiple requests for the same data into a single request
- Updating "out of date" data in the background
- Knowing when data is "out of date"
- Reflecting updates to data as quickly as possible
- Performance optimizations like pagination and lazy loading data
- Managing memory and garbage collection of server state
- Memoizing query results with structural sharing
一旦你掌握了应用中服务器状态的本质,你会遇到更多的挑战,例如:
- 缓存...(这可能是编程中最难的事情)
- 将对同一数据的多个请求的重复数据集合到单个请求中
- 在后台更新"过时"的数据
- 知道数据何时"过时"
- 尽可能快地反映数据更新
- 分页和延迟加载数据等性能优化
- 管理服务器状态的内存和 GC
- 结构化存储并共享查询结果
:::
## :triangular_flag_on_post: Explain Advantages of React Query
:::success
React Query is hands down one of the best libraries for managing server state. It works amazingly well out-of-the-box, with zero-config, and can be customized to your liking as your application grows.
React Query 无疑是管理服务器状态的最佳库之一。它非常好用,开箱即用,无需配置,并且可以随着应用的增长而根据自己的喜好进行定制。
:::
:::success
React Query allows you to defeat and overcome the tricky challenges and hurdles of server state and control your app data before it starts to control you.
React Query 使你可以击败并征服那些棘手的服务器状态挑战和障碍,并在混杂的数据开始打乱你的应用之前对其进行控制。
:::
:::success
On a more technical note, React Query will likely:
- Help you remove many lines of complicated and misunderstood code from your application and replace with just a handful of lines of React Query logic.
- Make your application more maintainable and easier to build new features without worrying about wiring up new server state data sources
- Have a direct impact on your end-users by making your application feel faster and more responsive than ever before.
- Potentially help you save on bandwidth and increase memory performance
从更专业的角度来说,React Query 可能会:
- 帮助您从应用中删除许多复杂和容易引起误解的代码行,用少量的 React Query 逻辑代替
- 使你的应用更易维护,更易构建新功能,而不必担心如何连接新的服务器状态数据源
- 通过维持应用的流畅 GUI 及更快的数据响应,直接影响最终用户
- 潜在地帮助你节省带宽和提高内存性能
:::
## :mag_right: Concepts
```jsx
// demo
import {
useQuery,
useMutation,
useQueryClient,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
import { getTodos, postTodo } from '../my-api'
// Create a client
const queryClient = new QueryClient()
function App() {
return (
// Provide the client to your App
<QueryClientProvider client={queryClient}>
<Todos />
</QueryClientProvider>
)
}
function Todos() {
// Access the client
const queryClient = useQueryClient()
// Queries
const query = useQuery({ queryKey: ['todos'], queryFn: getTodos })
// Mutations
const mutation = useMutation({
mutationFn: postTodo,
onSuccess: () => {
// Invalidate and refetch
queryClient.invalidateQueries({ queryKey: ['todos'] })
},
})
return (
<div>
<ul>{query.data?.map((todo) => <li key={todo.id}>{todo.title}</li>)}</ul>
<button
onClick={() => {
mutation.mutate({
id: Date.now(),
title: 'Do Laundry',
})
}}
>
Add Todo
</button>
</div>
)
}
render(<App />, document.getElementById('root'))
```
### :small_blue_diamond: Queries
:::info
:bulb: A query is a declarative dependency on an asynchronous source of data that is tied to a unique key. A query can be used with any Promise based method (including GET and POST methods) to fetch data from a server. If your method modifies data on the server, we recommend using Mutations instead.
查询是一种对于与唯一键值相关联的异步数据源的声明性依赖。 查询可以与任何基于 Promise 的方法(包括 GET 和 POST 方法)一起使用,以从服务器获取数据。 如果你的方法修改了服务器上的数据,建议你改用修改[^TanStack]
:::
| **name** | **explain** | **Note** |
|:--------:|:---------:|:--------:|
| useQuery | hook | need a unique key and fn |
| queryKey | unique key | |
| status | same using as bellow ||
| isPending / isError / isSuccess | status === 'pending' / 'error' / 'success' | |
| error | if isError | |
| data | if isSuccess | |
| isFetching | any state if fetching ||
|fetchStatus | fetchStatus === 'fetching' / 'paused' / 'idle' | 'paused': The query is not executing - it is paused until you have connection again; 'idle' - The query is not doing anything at the moment. |
|networkMode|online / always / offlineFirst| [doc](https://tanstack.com/query/latest/docs/framework/react/guides/network-mode) |
```jsx
const { isPending, isError, data, error } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodoList,
})
```
[^TanStack]: [TanStack Query Official doc](https://tanstack.com/query/latest/docs/framework/react/overview)