Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ssi02014/react-query-tutorial
๐ TanStack Query(aka. react query) ์์ ์์ฃผ ์ฌ์ฉ๋๋ ๊ฐ๋
์ ๋ฆฌ
https://github.com/ssi02014/react-query-tutorial
caching react react-query tanstack-query typescript
Last synced: 30 days ago
JSON representation
๐ TanStack Query(aka. react query) ์์ ์์ฃผ ์ฌ์ฉ๋๋ ๊ฐ๋ ์ ๋ฆฌ
- Host: GitHub
- URL: https://github.com/ssi02014/react-query-tutorial
- Owner: ssi02014
- Created: 2022-02-07T13:21:29.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2024-08-30T07:30:27.000Z (3 months ago)
- Last Synced: 2024-10-01T16:05:29.232Z (about 1 month ago)
- Topics: caching, react, react-query, tanstack-query, typescript
- Homepage:
- Size: 446 KB
- Stars: 1,393
- Watchers: 10
- Forks: 148
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# ๐ป TanStack Query(React)
- ํด๋น ์ ์ฅ์๋ TanStack Query(React)์์ ์์ฃผ ์ฌ์ฉํ๋ ๊ฐ๋ ๋ค์ ์ ๋ฆฌํ ์ ์ฅ์์ ๋๋ค. TanStack Query(React)์ ๋ชจ๋ ํ์ฉ ๋ฐฉ๋ฒ์ด ์์ฑ๋ ์ํ๋ ์๋๋ฉฐ, ํ์ํ ๋ด์ฉ์ ์ถ๊ฐ, ๋ณด์ํ ์์ ์ ๋๋ค.
## Contributors
- ๊ธฐ์ฌํด์ฃผ์ ๋ชจ๋ ๋ถ๊ป ๊ฐ์ฌ๋๋ฆฝ๋๋ค.
- ์คํ์, ๊ฐ๋ ์ฑ์ด ์ข์ง ์์ ๋ถ๋ถ ๋๋ ์ถ๊ฐ ๋ด์ฉ์ `Pull Request`, `Issue` ๋ฑ์ ์์ ๋กญ๊ฒ ๋จ๊ฒจ์ฃผ์๋ฉด ๊ฒํ ํ์ ๋ฐ์ํ๊ฒ ์ต๋๋ค.[![contributors](https://contrib.rocks/image?repo=ssi02014/react-query-tutorial)](https://github.com/ssi02014/react-query-tutorial/graphs/contributors)
## TanStack Query(React) v5
- โญ๏ธ TanStack Query(React) `v5`๊ฐ 23.10.17์ ๋ฆด๋ฆฌ์ฆ๋์ต๋๋ค. ํด๋น ๋ฌธ์๋ `v5` ๊ธฐ์ค์ผ๋ก ์์ฑ๋์ด ์์ต๋๋ค.
- โญ๏ธ ๊ธฐ์กด `v4` ๋ฌธ์๋ [react query tutorial v4 ๋ฌธ์](https://github.com/ssi02014/react-query-tutorial/tree/master/README.v4.md)๋ฅผ ํ์ธํด ์ฃผ์ธ์.![แแ ณแแ ณแ แ ตแซแแ ฃแบ 2023-10-18 แแ ฉแแ ฅแซ 2 09 09](https://github.com/ssi02014/react-query-tutorial/assets/64779472/84de2a61-7e39-4d52-aed8-b0ab67af95bc)
- `v3 -> v4`, `v4 -> v5` Migrating ์ ๋ฆฌ ๋ฌธ์๋ ์๋ ๋ฌธ์๋ค์ ํ์ธํด ์ฃผ์๊ธฐ ๋ฐ๋๋๋ค.
- [Migrating to TanStack Query(React) v5](https://github.com/ssi02014/react-query-tutorial/tree/master/document/v5.md)- [Migrating to TanStack Query(React) v4](https://github.com/ssi02014/react-query-tutorial/tree/master/document/v4.md)
## ์ฃผ์ ์ปจ์ ๋ฐ ๊ฐ์ด๋ ๋ชฉ์ฐจ
1. [React Query ๊ฐ์ ๋ฐ ๊ธฐ๋ฅ](#๊ฐ์)
2. [๊ธฐ๋ณธ ์ค์ (QueryClientProvider, QueryClient)](#react-query-๊ธฐ๋ณธ-์ค์ )
3. [React Query Devtools](#devtools)
4. [React Query ์บ์ฑ ๋ผ์ดํ ์ฌ์ดํด](#์บ์ฑ-๋ผ์ดํ-์ฌ์ดํด)
5. [useQuery](#usequery)
6. [useQuery ์ฃผ์ ๋ฆฌํด ๋ฐ์ดํฐ](#usequery-์ฃผ์-๋ฆฌํด-๋ฐ์ดํฐ)
7. [staleTime๊ณผ gcTime](#staletime๊ณผ-gctime)
8. [๋ง์ดํธ ๋ ๋๋ง๋ค ์ฌ์์ฒญํ๋ refetchOnMount](#refetchonmount)
9. [์๋์ฐ๊ฐ ํฌ์ปค์ฑ ๋ ๋๋ง๋ค ์ฌ์์ฒญํ๋ refetchOnWindowFocus](#refetchonwindowfocus)
10. [Polling ๋ฐฉ์์ ๊ตฌํํ๊ธฐ ์ํ refetchInterval์ refetchIntervalInBackground)](#polling)
11. [์๋ ์คํ์ enabled์ ์๋์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ๋ค์ ์์ฒญํ๋ refetch](#enabled-refetch)
12. [์คํจํ ์ฟผ๋ฆฌ์ ๋ํด ์ฌ์์ฒญํ๋ retry](#retry)
13. [onSuccess, onError, onSettled](#onsuccess-onerror-onsettled) - ๐ก **v5 @Deprecated**
14. [select๋ฅผ ์ด์ฉํ ๋ฐ์ดํฐ ๋ณํ](#select)
15. [์ฟผ๋ฆฌ๊ฐ pending ์ํ์ธ ๋์ ๋ณด์ฌ ์ค ์ ์๋ placeholderData](#placeholderdata)
16. [Paginated ๊ตฌํ์ ์ ์ฉํ keepPreviousData](#keepPreviousData) - ๐ก **v5 @Deprecated**
17. [ํน์ ์ฟผ๋ฆฌ ํ๋กํผํฐ ๋ณ๊ฒฝ ์์๋ง ๋ฆฌ๋ ๋๋ง์ ํธ๋ฆฌ๊ฑฐ ํ ์ ์๋ notifyOnChangeProps](#notifyOnChangeProps)
18. [์ฟผ๋ฆฌ๋ฅผ ๋ณ๋ ฌ(Parallel) ์์ฒญํ ์ ์๋ useQueries](#parallel)
19. [์ข ์ ์ฟผ๋ฆฌ(Dependent Queries)](#dependent-queries)
20. [QueryClient ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ useQueryClient](#usequeryclient)
21. [์ด๊ธฐ ๋ฐ์ดํฐ๋ฅผ ์ค์ ํ ์ ์๋ initialData](#initial-query-data)
22. [๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ถ๋ฌ์ค๋ PreFetching](#prefetching)
23. [Infinite Queries(๋ฌดํ ์ฟผ๋ฆฌ) + useInfiniteQuery](#infinite-queries)
24. [์๋ฒ์ HTTP CUD๊ด๋ จ ์์ ์ ์ํ useMutation](#usemutation)
25. [์ฟผ๋ฆฌ ์๋ ์ทจ์ cancelQueries](#cancelqueries)
26. [์ฟผ๋ฆฌ๋ฅผ ๋ฌดํจํํ ์ ์๋ queryClient.invalidateQueries](#์ฟผ๋ฆฌ-๋ฌดํจํ)
27. [์บ์ ๋ฐ์ดํฐ ์ฆ์ ์ ๋ฐ์ดํธ๋ฅผ ์ํ queryClient.setQueryData](#์บ์-๋ฐ์ดํฐ-์ฆ์-์ ๋ฐ์ดํธ)
28. [์ฌ์ฉ์ ๊ฒฝํ(UX)์ ์ฌ๋ ค์ฃผ๋ Optimistic Updates(๋๊ด์ ์ ๋ฐ์ดํธ)](#optimistic-update)
29. [์๋ฌ๊ฐ ๋ฐ์ํ์ ๋ Fallback UI๋ฅผ ์ ์ธ์ ์ผ๋ก ๋ณด์ฌ์ฃผ๊ธฐ ์ํ ErrorBoundary + useQueryErrorResetBoundary](#usequeryerrorresetboundary)
30. [์๋ฒ ๋ก๋ฉ ์ค์ผ ๋ Fallback UI๋ฅผ ์ ์ธ์ ์ผ๋ก ๋ณด์ฌ์ฃผ๊ธฐ ์ํ Suspense](#suspense)
31. [์ฑ ์ ์ฒด์ ๋์ผํ ์ฟผ๋ฆฌ ํจ์๋ฅผ ๊ณต์ ํ๋ Default Query Function](#default-query-function)
32. [๋ฆฌ์กํธ ์ฟผ๋ฆฌ์ ํ์ ์คํฌ๋ฆฝํธ ์ ์ฉ](#react-query-typescript)
33. [๋ฆฌ์กํธ ์ฟผ๋ฆฌ ESLint ์ ์ฉ](#react-query-eslint-plugin)
34. [๋ฆฌ์กํธ ์ฟผ๋ฆฌ ์ง์ ๋ฒ์ ](#์ง์-๋ฒ์ )
## ๐ ๊ธฐํ ์ฐธ๊ณ ๋ฌธ์
1. [QueryClient ์ฃผ์ ๋ด์ฉ ์ ๋ฆฌ ๋ฌธ์](https://github.com/ssi02014/react-query-tutorial/tree/master/document/queryClient.md)
2. [๊ธฐ๋ณธ์ ์ธ React Query ์ํคํ ์ฒ ์ดํด๋ณด๊ธฐ: inside React Query](https://github.com/ssi02014/react-query-tutorial/tree/master/document/insideReactQuery.md)
## ๐จ๐ปโ๐ป ์ฃผ์ ์ฐธ๊ณ ํ์ด์ง
- [TanStack/query ๊ณต์ ๊นํ๋ธ](https://github.com/TanStack/query)
- [TkDodo ๋ธ๋ก๊ทธ(TanStack Query maintainer)](https://tkdodo.eu/blog/)
## ๐ React Query ๊ฐ์ ๋ฐ ๊ธฐ๋ฅ
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
### ๊ฐ์
- react-query๋ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์์ `์๋ฒ ์ํ ๊ฐ์ ธ์ค๊ธฐ`, `์บ์ฑ`, `๋๊ธฐํ ๋ฐ ์ ๋ฐ์ดํธ`๋ฅผ ๋ณด๋ค ์ฝ๊ฒ ๋ค๋ฃฐ ์ ์๋๋ก ๋์์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. ํด๋ผ์ด์ธํธ ์ํ์ ์๋ฒ ์ํ๋ฅผ ๋ช ํํ ๊ตฌ๋ถํ๊ธฐ ์ํด ๋ง๋ค์ด์ก๋ค.
- react-query์์๋ ๊ธฐ์กด ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ `redux`, `mobX`๊ฐ `ํด๋ผ์ด์ธํธ ์ํ ์์ `์ ์ ํฉํ์ง๋ง, `๋น๋๊ธฐ ๋๋ ์๋ฒ ์ํ ์์ `์๋ ๊ทธ๋ค์ง ์ข์ง ์๋ค๊ณ ์ธ๊ธํ๋ค.
- ํด๋ผ์ด์ธํธ ์ํ(Client State)์ ์๋ฒ ์ํ(Server State)๋ ์์ ํ ๋ค๋ฅธ ๊ฐ๋ ์ด๋ฉฐ, ํด๋ผ์ด์ธํธ ์ํ๋ ๊ฐ๊ฐ์ input ๊ฐ์ผ๋ก ์๋ฅผ ๋ค ์ ์๊ณ , ์๋ฒ ์ํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋์ด ์๋ ๋ฐ์ดํฐ๋ก ์๋ฅผ ๋ค ์ ์๋ค.
### ๊ธฐ๋ฅ
- ์บ์ฑ
- ๋์ผํ ๋ฐ์ดํฐ์ ๋ํ ์ค๋ณต ์์ฒญ์ ๋จ์ผ ์์ฒญ์ผ๋ก ํตํฉ
- ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์ค๋๋ ๋ฐ์ดํฐ ์ ๋ฐ์ดํธ
- ๋ฐ์ดํฐ๊ฐ ์ผ๋ง๋ ์ค๋๋์๋์ง ์ ์ ์๋ค.
- ๋ฐ์ดํฐ ์ ๋ฐ์ดํธ๋ฅผ ๊ฐ๋ฅํ ๋น ๋ฅด๊ฒ ๋ฐ์
- ํ์ด์ง๋ค์ด์ ๋ฐ ๋ฐ์ดํฐ ์ง์ฐ ๋ก๋์ ๊ฐ์ ์ฑ๋ฅ ์ต์ ํ
- ์๋ฒ ์ํ์ ๋ฉ๋ชจ๋ฆฌ ๋ฐ ๊ฐ๋น์ง ์์ง ๊ด๋ฆฌ
- ๊ตฌ์กฐ ๊ณต์ ๋ฅผ ์ฌ์ฉํ์ฌ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ๋ชจํ
## React Query ๊ธฐ๋ณธ ์ค์
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- [QueryClient ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/reference/QueryClient)
- [QueryClientProvider ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/reference/QueryClientProvider)```tsx
import { QueryClient } from "@tanstack/react-query";const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity,
// ...
},
},
});
```- QueryClient๋ฅผ ์ฌ์ฉํ์ฌ `์บ์`์ ์ํธ ์์ฉํ ์ ์๋ค.
- QueryClient์์ ๋ชจ๋ `query` ๋๋ `mutation`์ ๊ธฐ๋ณธ ์ต์ ์ ์ถ๊ฐํ ์ ์์ผ๋ฉฐ, ์ข ๋ฅ๊ฐ ์๋นํ๋ฏ๋ก ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํด ๋ณด์.```tsx
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";const queryClient = new QueryClient({ /* options */});
function App() {
return (
๋ธ๋ผ๋ธ๋ผ
;
);
}
```- react-query๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ `QueryClientProvider`๋ฅผ ์ต์๋จ์์ ๊ฐ์ธ์ฃผ๊ณ `QueryClient` ์ธ์คํด์ค๋ฅผ client props๋ก ๋ฃ์ด ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฐ๊ฒฐํด์ผ ํ๋ค.
- ์ ์์์์ App.js์ QueryClientProvider๋ก ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ธ๊ณ , client props์๋ค queryClient๋ฅผ ์ฐ๊ฒฐํจ์ผ๋ก์จ, ์ด context๋ ์ฑ์์ ๋น๋๊ธฐ ์์ฒญ์ ์์์ ์ฒ๋ฆฌํ๋ `background` ๊ณ์ธต์ด ๋๋ค.
## Devtools
![แแ ณแแ ณแ แ ตแซแแ ฃแบ 2022-04-07 แแ ฉแแ ฎ 11 53 32](https://user-images.githubusercontent.com/64779472/162228222-d1c7dd3e-ce62-484d-bfa0-8493f3e68cae.png)
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- [React Query Devtools ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/devtools)
- react-query๋ `์ ์ฉ devtools`๋ฅผ ์ ๊ณตํ๋ฉฐ ๋ณ๋์ ํจํค์ง ์ค์น๊ฐ ํ์ํ๋ค.
- devtools๋ฅผ ์ฌ์ฉํ๋ฉด React Query์ ๋ชจ๋ ๋ด๋ถ ๋์์ `์๊ฐํ`ํ๋ ๋ฐ ๋์์ด ๋๋ฉฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ฉด `๋๋ฒ๊น ์๊ฐ์ ์ ์ฝ`ํ ์ ์๋ค.
- devtools๋ ๊ธฐ๋ณธ๊ฐ์ผ๋ก `process.env.NODE_ENV === "development"` ์ธ ๊ฒฝ์ฐ์๋ง ์คํ๋๋ค, ์ฆ ์ผ๋ฐ์ ์ผ๋ก ๊ฐ๋ฐ ํ๊ฒฝ์์๋ง ์๋ํ๋๋ก ์ค์ ๋์ด ์์ผ๋ฏ๋ก, ํ๋ก์ ํธ ๋ฐฐํฌ ์์ Devtools ์ฝ์ ์ฝ๋๋ฅผ ์ ๊ฑฐํด ์ค ํ์๊ฐ ์๋ค.
- Next 13+์ App Dir์์ dev dependency๋ก ์ค์นํด์ผ ๋์ํ๋ค.```bash
$ npm i @tanstack/react-query-devtools
# or
$ pnpm add @tanstack/react-query-devtools
# or
$ yarn add @tanstack/react-query-devtools
# or
$ bun add @tanstack/react-query-devtools
``````tsx
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";function App() {
return (
{/* The rest of your application */}
);
}
```### options
- initialIsOpen (Boolean)
- `true`์ด๋ฉด ๊ฐ๋ฐ ๋๊ตฌ๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์ด๋ ค ์๋๋ก ์ค์ ํ ์ ์๋ค.
- buttonPosition?: ("top-left" | "top-right" | "bottom-left" | "bottom-right" | "relative")
- ๊ธฐ๋ณธ๊ฐ: `bottom-right`
- devtools ํจ๋์ ์ฌ๋ซ๊ธฐ ์ํ ๋ก๊ณ ์์น
- `relative`์ผ ๋ ๋ฒํผ์ devtools๋ฅผ ๋ ๋๋งํ๋ ์์น์ ๋ฐฐ์น๋๋ค.
- ์ผ๋ฐ์ ์ผ๋ก initialIsOpen, buttonPosition์ ์์ฃผ ์ฌ์ฉํ๋ฉฐ ๊ทธ ์ธ์ position, client์ ๊ฐ์ ์ต์ ๋ค๋ ์กด์ฌํ๋ค.
## ์บ์ฑ ๋ผ์ดํ ์ฌ์ดํด
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- [React Query ์บ์ ๋ผ์ดํ ์ฌ์ดํด ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/guides/caching)
```
* Query Instances with and without cache data(์บ์ ๋ฐ์ดํฐ๊ฐ ์๊ฑฐ๋ ์๋ ์ฟผ๋ฆฌ ์ธ์คํด์ค)
* Background Refetching(๋ฐฑ๊ทธ๋ผ์ด๋ ๋ฆฌํจ์นญ)
* Inactive Queries(๋นํ์ฑ ์ฟผ๋ฆฌ)
* Garbage Collection(๊ฐ๋น์ง ์ปฌ๋ ์ )
```- `gcTime`์ ๊ธฐ๋ณธ๊ฐ 5๋ถ, `staleTime` ๊ธฐ๋ณธ๊ฐ 0์ด๋ฅผ ๊ฐ์
1. `A`๋ผ๋ queryKey๋ฅผ ๊ฐ์ง A ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ `mount`๋จ
2. ๋คํธ์ํฌ์์ ๋ฐ์ดํฐ fetchํ๊ณ , ๋ถ๋ฌ์จ ๋ฐ์ดํฐ๋ A๋ผ๋ queryKey๋ก `์บ์ฑ`ํจ
3. ์ด ๋ฐ์ดํฐ๋ `fresh`์ํ์์ `staleTime(๊ธฐ๋ณธ๊ฐ 0)` ์ดํ `stale` ์ํ๋ก ๋ณ๊ฒฝ๋จ
4. A ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ `unmount`๋จ
5. ์บ์๋ `gcTime(๊ธฐ๋ณธ๊ฐ 5min)` ๋งํผ ์ ์ง๋๋ค๊ฐ `๊ฐ๋น์ง ์ฝ๋ ํฐ(GC)`๋ก ์์ง๋จ
6. ๋ง์ผ, gcTime ์ง๋๊ธฐ ์ ์ด๊ณ , A ์ฟผ๋ฆฌ ์ธ์คํด์ค freshํ ์ํ๋ผ๋ฉด ์๋กญ๊ฒ mount๋๋ฉด ์บ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค๋ค.
## useQuery
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
### useQuery ๊ธฐ๋ณธ ๋ฌธ๋ฒ
- [useQuery ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/reference/useQuery)
- useQuery๋ `v5`๋ถํฐ ์ธ์๋ก ๋จ ํ๋์ `๊ฐ์ฒด`๋ง ๋ฐ๋๋ค. ๊ทธ์ค์ ์ฒซ ๋ฒ์งธ ์ธ์๊ฐ `queryKey`, `queryFn`๊ฐ ํ์ ๊ฐ์ด๋ค.```tsx
const result = useQuery({
queryKey, // required
queryFn, // required
// ...options ex) gcTime, staleTime, select, ...
});result.data;
result.isLoading;
result.refetch;
// ...
``````tsx
// ์ค์ ์์
// ๐ก queryFn์ ๋ฐํ ํ์ ์ ์ง์ ํด์ฃผ๋ฉด useQuery์ ํ์ ์ถ๋ก ์ด ์ํํฉ๋๋ค.
const getAllSuperHero = async (): Promise> => {
return await axios.get("http://localhost:4000/superheroes");
};const { data, isLoading } = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
});
```
**1. queryKey**
```tsx
// (1) queryKey๋ ๋ฐ์ดํฐ๋ฅผ ๊ณ ์ ํ๊ฒ ์๋ณ์ ๋ํด ์ฟผ๋ฆฌ ํจ์์ ์๋์ ๊ฐ์ด ํธ๋ฆฌํ๊ฒ ์ ๋ฌํ ์๋ ์๋ค.
const getSuperHero = async ({
queryKey,
}: {
queryKey: ["super-hero", number];
}): Promise> => {
const heroId = queryKey[1]; // ex) queryKey: ["super-hero", "3"]return await axios.get(`http://localhost:4000/superheroes/${heroId}`);
};const useSuperHeroData = (heroId: string) => {
return useQuery({
queryKey: ["super-hero", heroId],
queryFn: getSuperHero, // (*)
});
};
```- useQuery์ queryKey๋ `๋ฐฐ์ด`๋ก ์ง์ ํด ์ค์ผ ํ๋ค.
- ์ด๋ ๋จ์ผ ๋ฌธ์์ด๋ง ํฌํจ๋ ๋ฐฐ์ด์ด ๋ ์๋ ์๊ณ , ์ฌ๋ฌ ๋ฌธ์์ด๊ณผ ์ค์ฒฉ๋ ๊ฐ์ฒด๋ก ๊ตฌ์ฑ๋ ๋ณต์กํ ํํ์ผ ์๋ ์๋ค.```tsx
// An individual todo
useQuery({ queryKey: ["todo", 5], ... })// An individual todo in a "preview" format
useQuery({ queryKey: ["todo", 5, { preview: true }], ...})
```- useQuery๋ `queryKey`๋ฅผ ๊ธฐ๋ฐ์ผ๋ก `์ฟผ๋ฆฌ ์บ์ฑ`์ ๊ด๋ฆฌํ๋ ๊ฒ์ด ํต์ฌ์ด๋ค.
- ๋ง์ฝ, ์ฟผ๋ฆฌ๊ฐ ํน์ ๋ณ์์ `์์กด`ํ๋ค๋ฉด ๋ฐฐ์ด์๋ค ์ด์ด์ ์ค์ผ ํ๋ค. `ex: ["super-hero", heroId, ...]`
- **์ด๋ ์ฌ์ค ๊ต์ฅํ ์ค์ํ๋ค.** ์๋ฅผ ๋ค์ด, `queryClient.setQueryData` ๋ฑ๊ณผ ๊ฐ์ด ํน์ ์ฟผ๋ฆฌ์ ์ ๊ทผ์ด ํ์ ํ ๋ `์ด๊ธฐ์ ์ค์ ํด๋ ํฌ๋งท`์ ์ง์ผ์ค์ผ ์ ๋๋ก ์ฟผ๋ฆฌ์ ์ ๊ทผํ ์ ์๋ค.
- ์๋ options ์์ ๋ฅผ ์ดํด๋ณด๋ฉด useSuperHeroData์ queryKey๋ `["super-hero", heroId]`์ด๋ค. ๊ทธ๋ ๋ค๋ฉด queryClient.setQueryData๋ฅผ ์ด์ฉํ ๋ ๋๊ฐ์ด `["super-hero", heroId]` ํฌ๋งท์ ๊ฐ์ ธ์ผ ํ๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ์ํ๋ ์ฟผ๋ฆฌ์ ์ ๊ทผํ ์ ์๋ค.
**2. queryFn**
- useQuery์ queryFn๋ `Promise`๋ฅผ ๋ฐํํ๋ ํจ์๋ฅผ ๋ฃ์ด์ผ ํ๋ค.
```tsx
// (2) ์๋จ์ queryKey ์์ ์ ๋ฐ๋๋ก queryFn ์์ฒด์ ์ผ๋ก ์ธ์๋ฅผ ๋ฐ๋ ํํ
const getSuperHero = async (heroId: string): Promise> => {
return await axios.get(`http://localhost:4000/superheroes/${heroId}`);
};const useSuperHeroData = (heroId: string) => {
return useQuery({
queryKey: ["super-hero", heroId],
queryFn: () => getSuperHero(heroId), // (*)
});
};
```
**3. options**
- [useQuery ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/reference/useQuery)
- useQuery์ `options`์ ๋ง์ด ์ฐ์ด๋ ์ต์ ๋ค์ ์ฐจ๊ทผ์ฐจ๊ทผ ์ดํด๋ณผ ์์ ์ด๋ค. ๋ฌธ์ ์ธ์ ๋์ฑ ์์ธํ ์๊ณ ์ถ๋ค๋ฉด ์ ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์.
```tsx
const useSuperHeroData = (heroId: string) => {
return useQuery({
queryKey: ["super-hero", heroId],
queryFn: () => getSuperHero(heroId),
gcTime: 5 * 60 * 1000, // 5๋ถ
staleTime: 1 * 60 * 1000, // 1๋ถ
retry: 1,
// ... options
});
};
```
### useQuery ์ฃผ์ ๋ฆฌํด ๋ฐ์ดํฐ
- [useQuery ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/reference/useQuery)
```tsx
const {
data,
error,
status,
fetchStatus,
isLoading,
isFetching,
isError,
refetch,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
});
```- **data**: ์ฟผ๋ฆฌ ํจ์๊ฐ ๋ฆฌํดํ `Promise`์์ `resolved`๋ ๋ฐ์ดํฐ
- **error**: ์ฟผ๋ฆฌ ํจ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ, ์ฟผ๋ฆฌ์ ๋ํ ์ค๋ฅ ๊ฐ์ฒด
- **status**: `data`, ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ์ ๋ํ ์ํ๋ฅผ ํํํ๋ status๋ ๋ฌธ์์ด ํํ๋ก `3๊ฐ์ง`์ ๊ฐ์ด ์กด์ฌํ๋ค.
- pending: ์ฟผ๋ฆฌ ๋ฐ์ดํฐ๊ฐ ์๊ณ , ์ฟผ๋ฆฌ ์๋๊ฐ ์์ง ์๋ฃ๋์ง ์์ ์ํ.
- `{ enabled: false }` ์ํ๋ก ์ฟผ๋ฆฌ๊ฐ ํธ์ถ๋๋ฉด ์ด ์ํ๋ก ์์๋๋ค.
- [Dependent Queries ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/guides/dependent-queries)
- error: ์๋ฌ ๋ฐ์ํ์ ๋ ์ํ
- success: ์ฟผ๋ฆฌ ํจ์๊ฐ ์ค๋ฅ ์์ด ์์ฒญ ์ฑ๊ณตํ๊ณ ๋ฐ์ดํฐ๋ฅผ ํ์ํ ์ค๋น๊ฐ ๋ ์ํ.
- **fetchStatus**: `queryFn`์ ๋ํ ์ ๋ณด๋ฅผ ๋ํ๋
- fetching: ์ฟผ๋ฆฌ๊ฐ ํ์ฌ ์คํ ์ค์ธ ์ํ
- paused: ์ฟผ๋ฆฌ๋ฅผ ์์ฒญํ์ง๋ง, ์ ์ ์ค๋จ๋ ์ํ (network mode์ ์ฐ๊ด)
- idle: ์ฟผ๋ฆฌ๊ฐ ํ์ฌ ์๋ฌด ์์ ๋ ์ํํ์ง ์๋ ์ํ
- **isLoading**: `์บ์ฑ ๋ ๋ฐ์ดํฐ๊ฐ ์์ ๋` ์ฆ, ์ฒ์ ์คํ๋ ์ฟผ๋ฆฌ์ผ ๋ ๋ก๋ฉ ์ฌ๋ถ์ ๋ฐ๋ผ `true/false`๋ก ๋ฐํ๋๋ค.
- ์ด๋ ์บ์ฑ ๋ ๋ฐ์ดํฐ๊ฐ ์๋ค๋ฉด ๋ก๋ฉ ์ฌ๋ถ์ ์๊ด์์ด `false`๋ฅผ ๋ฐํํ๋ค.
- `isFetching && isPending` ์ ๋์ผํ๋ค.
- **isFetching**: ์บ์ฑ ๋ ๋ฐ์ดํฐ๊ฐ ์๋๋ผ๋ ์ฟผ๋ฆฌ๊ฐ ์คํ๋๋ฉด ๋ก๋ฉ ์ฌ๋ถ์ ๋ฐ๋ผ `true/false`๋ก ๋ฐํ๋๋ค.
- **isSuccess**: ์ฟผ๋ฆฌ ์์ฒญ์ด ์ฑ๊ณตํ๋ฉด `true`
- **isError**: ์ฟผ๋ฆฌ ์์ฒญ ์ค์ ์๋ฌ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ `true`
- **refetch**: ์ฟผ๋ฆฌ๋ฅผ ์๋์ผ๋ก ๋ค์ ๊ฐ์ ธ์ค๋ ํจ์.
- **๊ทธ ์ธ ๋ฐํ ๋ฐ์ดํฐ๋ค์ ์์ธํ ์๊ณ ์ถ์ผ๋ฉด useQuery ๊ณต์ ๋ฌธ์ ์ฐธ๊ณ **
### ๐ก status, fetchStatus ๋๋ ์ ๋ค๋ฃจ๋ ๊ฑธ๊น?
- [Why Two Different States ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/guides/queries#why-two-different-states)
- fetchStatus๋ HTTP ๋คํธ์ํฌ ์ฐ๊ฒฐ ์ํ์ ์ข ๋ ๊ด๋ จ๋ ์ํ ๋ฐ์ดํฐ์ด๋ค.
- ์๋ฅผ ๋ค์ด, status๊ฐ `success` ์ํ๋ผ๋ฉด ์ฃผ๋ก fetchStatus๋ `idle` ์ํ์ง๋ง, ๋ฐฑ๊ทธ๋ผ์ด๋์์ re-fetch๊ฐ ๋ฐ์ํ ๋ `fetching` ์ํ์ผ ์ ์๋ค.
- status๊ฐ ๋ณดํต `loading` ์ํ์ผ ๋ fetchStatus๋ ์ฃผ๋ก `fetching`๋ฅผ ๊ฐ์ง๋ง, ๋คํธ์ํฌ ์ฐ๊ฒฐ์ด ๋์ด ์์ง ์์ ๊ฒฝ์ฐ `paused` ์ํ๋ฅผ ๊ฐ์ง ์ ์๋ค.
- ์ ๋ฆฌํ์๋ฉด ์๋์ ๊ฐ๋ค.
- status๋ `data`๊ฐ ์๋์ง ์๋์ง์ ๋ํ ์ํ๋ฅผ ์๋ฏธํ๋ค.
- fetchStatus๋ ์ฟผ๋ฆฌ ์ฆ, `queryFn ์์ฒญ`์ด ์งํ ์ค์ธ์ง ์๋์ง์ ๋ํ ์ํ๋ฅผ ์๋ฏธํ๋ค.
## useQuery ์ฃผ์ ์ต์
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- [useQuery ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/reference/useQuery)
### staleTime๊ณผ gcTime
- stale์ ์ฉ์ด ๋ป๋๋ก `์ฉ์`์ด๋ผ๋ ์๋ฏธ์ด๋ค. ์ฆ, ์ต์ ์ํ๊ฐ ์๋๋ผ๋ ์๋ฏธ์ด๋ค.
- fresh๋ ๋ป ๊ทธ๋๋ก `์ ์ ํ`์ด๋ผ๋ ์๋ฏธ์ด๋ค. ์ฆ, ์ต์ ์ํ๋ผ๋ ์๋ฏธ์ด๋ค.```tsx
const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
gcTime: 5 * 60 * 1000, // 5๋ถ
staleTime: 1 * 60 * 1000, // 1๋ถ
});
```
1. staleTime: `(number | Infinity)`
- staleTime์ ๋ฐ์ดํฐ๊ฐ `fresh์์ stale` ์ํ๋ก ๋ณ๊ฒฝ๋๋ ๋ฐ ๊ฑธ๋ฆฌ๋ ์๊ฐ, ๋ง์ฝ staleTime์ด `3000`์ด๋ฉด fresh ์ํ์์ `3์ด` ๋ค์ stale๋ก ๋ณํ
- `fresh` ์ํ์ผ ๋๋ ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ ์๋กญ๊ฒ mount ๋์ด๋ ๋คํธ์ํฌ ์์ฒญ(fetch)์ด ์ผ์ด๋์ง ์๋๋ค.
- ์ฐธ๊ณ ๋ก, staleTime์ ๊ธฐ๋ณธ๊ฐ์ `0`์ด๊ธฐ ๋๋ฌธ์ ์ผ๋ฐ์ ์ผ๋ก fetch ํ์ ๋ฐ๋ก stale์ด ๋๋ค.
2. gcTime: `(number | Infinity)`
- ๋ฐ์ดํฐ๊ฐ ์ฌ์ฉํ์ง ์๊ฑฐ๋, `inactive` ์ํ์ผ ๋ `์บ์ฑ ๋ ์ํ๋ก` ๋จ์์๋ ์๊ฐ(๋ฐ๋ฆฌ์ด)์ด๋ค.
- ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ unmount ๋๋ฉด ๋ฐ์ดํฐ๋ `inactive ์ํ๋ก ๋ณ๊ฒฝ`๋๋ฉฐ, ์บ์๋ `gcTime`๋งํผ ์ ์ง๋๋ค.
- gcTime์ด ์ง๋๋ฉด `๊ฐ๋น์ง ์ฝ๋ ํฐ`๋ก ์์ง๋๋ค.
- gcTime์ด ์ง๋๊ธฐ ์ ์ ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ ๋ค์ mount ๋๋ฉด, ๋ฐ์ดํฐ๋ฅผ fetch ํ๋ ๋์ ์บ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค๋ค.
- gcTime์ staleTime๊ณผ ๊ด๊ณ์์ด, ๋ฌด์กฐ๊ฑด `inactive` ๋ ์์ ์ ๊ธฐ์ค์ผ๋ก ์บ์ ๋ฐ์ดํฐ ์ญ์ ๋ฅผ ๊ฒฐ์ ํ๋ค.
- gcTime์ ๊ธฐ๋ณธ๊ฐ์ `5๋ถ`์ด๋ค. SSR ํ๊ฒฝ์์๋ `Infinity`์ด๋ค.- ์ฌ๊ธฐ์ ์ฃผ์ํ ์ ์ staleTime๊ณผ gcTime์ ๊ธฐ๋ณธ๊ฐ์ ๊ฐ๊ฐ `0๋ถ`๊ณผ `5๋ถ`์ด๋ค. ๋ฐ๋ผ์ staleTime์ ์ด๋ ํ ์ค์ ๋ ํ์ง ์์ผ๋ฉด ํด๋น ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ(Observer)๊ฐ mount ๋์ ๋ ๋งค๋ฒ ๋ค์ API๋ฅผ ์์ฒญํ ๊ฒ์ด๋ค.
- staleTime์ gcTime๋ณด๋ค ๊ธธ๊ฒ ์ค์ ํ๋ค๊ณ ๊ฐ์ ํ๋ฉด, staleTime๋งํผ์ ์บ์ฑ์ ๊ธฐ๋ํ์ ๋ ์ํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ป์ง ๋ชปํ ๊ฒ์ด๋ค. ์ฆ, ๋ ๊ฐ์ ์ต์ ์ ์ ์ ํ๊ฒ ์ค์ ํด ์ค์ผ ํ๋ค.
- ์ฐธ๊ณ ๋ก, [TkDodo์ reply](https://github.com/TanStack/query/discussions/1685#discussioncomment-1876723)์ ๋ฐ๋ฅด๋ฉด TkDodo๋ `staleTime์ gcTime๋ณด๋ค ์๊ฒ ์ค์ ํ๋ ๊ฒ์ด ์ข๋ค.`๋ ์๊ฒฌ์ ๋์ํ์ง ์๋๋ค๊ณ ํ๋ค.
- ์์ปจ๋, staleTime์ด 60๋ถ์ผ์ง๋ผ๋ ์ ์ ๊ฐ ์์ฃผ ์ฌ์ฉํ์ง ์๋ ๋ฐ์ดํฐ๋ผ๋ฉด ๊ตณ์ด gcTime์ 60๋ถ ์ด์์ผ๋ก ์ค์ ํ์ฌ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ญ๋นํ ํ์๊ฐ ์๋ค.
### refetchOnMount
```tsx
const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
refetchOnMount: true,
});
```- refetchOnMount: `boolean | "always" | ((query: Query) => boolean | "always")`
- refetchOnMount๋ ๋ฐ์ดํฐ๊ฐ `stale` ์ํ์ผ ๊ฒฝ์ฐ, mount๋ง๋ค `refetch`๋ฅผ ์คํํ๋ ์ต์ ์ด๋ค.
- refetchOnMount์ ๊ธฐ๋ณธ๊ฐ์ `true`์ด๋ค.
- `always`๋ก ์ค์ ํ๋ฉด ๋ง์ดํธ ์๋ง๋ค ๋งค๋ฒ refetch๋ฅผ ์คํํ๋ค.
- `false`๋ก ์ค์ ํ๋ฉด ์ต์ด fetch ์ดํ์๋ refetch ํ์ง ์๋๋ค.
### refetchOnWindowFocus
```tsx
const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
refetchOnWindowFocus: true,
});
```- refetchOnWindowFocus: `boolean | "always" | ((query: Query) => boolean | "always")`
- refetchOnWindowFocus๋ ๋ฐ์ดํฐ๊ฐ `stale` ์ํ์ผ ๊ฒฝ์ฐ `์๋์ฐ ํฌ์ปค์ฑ` ๋ ๋๋ง๋ค refetch๋ฅผ ์คํํ๋ ์ต์ ์ด๋ค.
- refetchOnWindowFocus์ ๊ธฐ๋ณธ๊ฐ์ `true`์ด๋ค.
- ์๋ฅผ ๋ค์ด, ํฌ๋กฌ์์ ๋ค๋ฅธ ํญ์ ๋๋ ๋ค๊ฐ ๋ค์ ์๋ ๋ณด๋ ์ค์ธ ํญ์ ๋๋ ์ ๋๋ ์ด ๊ฒฝ์ฐ์ ํด๋นํ๋ค. ์ฌ์ง์ด F12๋ก ๊ฐ๋ฐ์ ๋๊ตฌ ์ฐฝ์ ์ผ์ ๋คํธ์ํฌ ํญ์ด๋ , ์ฝ์ ํญ์ด๋ ๊ฐ๋ฐ์ ๋๊ตฌ ์ฐฝ์์ ๋๋ค๊ฐ ํ์ด์ง ๋ด๋ถ๋ฅผ ๋ค์ ํด๋ฆญํ์ ๋๋ ์ด ๊ฒฝ์ฐ์ ํด๋นํ๋ค.
- `always`๋ก ์ค์ ํ๋ฉด ํญ์ ์๋์ฐ ํฌ์ปค์ฑ ๋ ๋๋ง๋ค refetch๋ฅผ ์คํํ๋ค๋ ์๋ฏธ์ด๋ค.
### Polling
```tsx
const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
refetchInterval: 2000,
refetchIntervalInBackground: true,
});
``````
Polling(ํด๋ง)์ด๋?
์ค์๊ฐ ์น์ ์ํ ๊ธฐ๋ฒ์ผ๋ก "์ผ์ ํ ์ฃผ๊ธฐ(ํน์ ํ ์๊ฐ)"๋ฅผ ๊ฐ์ง๊ณ ์๋ฒ์ ์๋ต์ ์ฃผ๊ณ ๋ฐ๋ ๋ฐฉ์์ด ํด๋ง ๋ฐฉ์์ด๋ค.
react-query์์๋ "refetchInterval", "refetchIntervalInBackground"์ ์ด์ฉํด์ ๊ตฌํํ ์ ์๋ค.
```1. refetchInterval: `number | false | ((data: TData | undefined, query: Query) => number | false)`
- refetchInterval์ `์๊ฐ(ms)`๋ฅผ ๊ฐ์ผ๋ก ๋ฃ์ด์ฃผ๋ฉด ์ผ์ ์๊ฐ๋ง๋ค ์๋์ผ๋ก refetch๋ฅผ ์์ผ์ค๋ค.
2. refetchIntervalInBackground: `boolean`
- refetchIntervalInBackground๋ `refetchInterval`๊ณผ ํจ๊ป ์ฌ์ฉํ๋ ์ต์ ์ด๋ค.
- ํญ/์ฐฝ์ด ๋ฐฑ๊ทธ๋ผ์ด๋์ ์๋ ๋์ refetch ์์ผ์ค๋ค. ์ฆ, ๋ธ๋ผ์ฐ์ ์ focus ๋์ด ์์ง ์์๋ refetch๋ฅผ ์์ผ์ฃผ๋ ๊ฒ์ ์๋ฏธํ๋ค.
### enabled refetch
```tsx
const {
data,
refetch,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
enabled: false,
});const handleClickRefetch = useCallback(() => {
refetch();
}, [refetch]);return (
{data?.data.map((hero: Data) => (
{hero.name}
))}
Fetch Heroes
);
```- enabled: `boolean`
- enabled๋ `์ฟผ๋ฆฌ๊ฐ ์๋์ผ๋ก ์คํ๋์ง ์๋๋ก ํ ๋ ์ค์ `ํ ์ ์๋ค.
- enabled๋ฅผ `false`๋ฅผ ์ฃผ๋ฉด ์ฟผ๋ฆฌ๊ฐ ์๋ ์คํ๋์ง ์๋๋ค.
- useQuery ๋ฐํ ๊ฐ ์ค status๊ฐ `pending` ์ํ๋ก ์์ํ๋ค.
- refetch๋ ์ฟผ๋ฆฌ๋ฅผ `์๋`์ผ๋ก ๋ค์ ์์ฒญํ๋ ๊ธฐ๋ฅ์ด๋ค. ์ฟผ๋ฆฌ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ์ค๋ฅ๋ง ๊ธฐ๋ก๋๋ค.
- ์ค๋ฅ๋ฅผ ๋ฐ์์ํค๋ ค๋ฉด `throwOnError` ์์ฑ์ `true`๋ก ํด์ ์ ๋ฌํด์ผ ํ๋ค.
- ๋ณดํต ์๋์ผ๋ก ์ฟผ๋ฆฌ ์์ฒญ์ ํ์ง ์๊ณ `๋ฒํผ ํด๋ฆญ`์ด๋ ํน์ ์ด๋ฒคํธ๋ฅผ ํตํด ์์ฒญ์ ์๋ํ ๋ ๊ฐ์ด ์ฌ์ฉํ๋ค.
- ๐ก ์ฃผ์ํ ์ ์, `enabled: false`๋ฅผ ์คฌ๋ค๋ฉด `queryClient`๊ฐ ์ฟผ๋ฆฌ๋ฅผ ๋ค์ ๊ฐ์ ธ์ค๋ ๋ฐฉ๋ฒ ์ค `invalidateQueries`์ `refetchQueries`๋ฅผ ๋ฌด์ํ๋ค.
### retry
```tsx
const {
data,
refetch,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
retry: 10, // ์ค๋ฅ๋ฅผ ํ์ํ๊ธฐ ์ ์ ์คํจํ ์์ฒญ์ 10๋ฒ ์ฌ์๋ํฉ๋๋ค.
});
```- retry: `(boolean | number | (failureCount: number, error: TError) => boolean)`
- retry๋ ์ฟผ๋ฆฌ๊ฐ `์คํจ`ํ๋ฉด useQuery๋ฅผ `ํน์ ํ์`๋งํผ ์ฌ์์ฒญํ๋ ์ต์ ์ด๋ค.
- retry๊ฐ `false`์ธ ๊ฒฝ์ฐ, ์คํจํ ์ฟผ๋ฆฌ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ค์ ์๋ํ์ง ์๋๋ค. `true`์ธ ๊ฒฝ์ฐ์๋ ์คํจํ ์ฟผ๋ฆฌ์ ๋ํด์ ๋ฌดํ ์ฌ์์ฒญ์ ์๋ํ๋ค.
- ๊ฐ์ผ๋ก `์ซ์`๋ฅผ ๋ฃ์ ๊ฒฝ์ฐ, ์คํจํ ์ฟผ๋ฆฌ๊ฐ ํด๋น ์ซ์๋ฅผ ์ถฉ์กฑํ ๋๊น์ง ์์ฒญ์ ์ฌ์๋ํ๋ค.
- ๊ธฐ๋ณธ๊ฐ์ ํด๋ผ์ด์ธํธ ํ๊ฒฝ์์๋ `3`, ์๋ฒ ํ๊ฒฝ์์๋ `0`์ด๋ค.
### onSuccess, onError, onSettled
- _NOTE_: `v4`๊น์ง ์๋ onSuccess, onError, onSettled Callback์ `useQuery` ์ต์ ์์ [@Deprecated](https://github.com/TanStack/query/pull/5353) ๋๋ค. ๋จ, `useMutation`์์๋ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
- [Breaking React Query's API on purpose](https://velog.io/@cnsrn1874/breaking-react-querys-api-on-purpose) ์ฐธ๊ณ
### select
```tsx
const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
select: (data) => {
const superHeroNames = data.data.map((hero) => hero.name);
return superHeroNames;
},
});return (
{data.map((heroName, idx) => (
{heroName}
))}
);
```- select: `(data: TData) => unknown`
- select ์ต์ ์ ์ฌ์ฉํ์ฌ ์ฟผ๋ฆฌ ํจ์์์ `๋ฐํ๋ ๋ฐ์ดํฐ์ ์ผ๋ถ๋ฅผ ๋ณํํ๊ฑฐ๋ ์ ํํ ์ ์๋ค.`
- ์ฐธ๊ณ ๋ก, ๋ฐํ๋ ๋ฐ์ดํฐ ๊ฐ์๋ ์ํฅ์ ์ฃผ์ง๋ง ์ฟผ๋ฆฌ ์บ์์ ์ ์ฅ๋๋ ๋ด์ฉ์๋ ์ํฅ์ ์ฃผ์ง ์๋๋ค.
### placeholderData
```tsx
const placeholderData = useMemo(() => generateFakeHeroes(), []);const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
placeholderData: placeholderData,
});
```- placeholderData: `TData | (previousValue: TData | undefined; previousQuery: Query | undefined,) => TData`
- placeholderData๋ฅผ ์ค์ ํ๋ฉด ์ฟผ๋ฆฌ๊ฐ `pending` ์ํ์ธ ๋์ ํน์ ์ฟผ๋ฆฌ์ ๋ํ placeholder data๋ก ์ฌ์ฉ๋๋ค.
- placeholderData๋ ์บ์์ ์ ์ง๋์ง ์์ผ๋ฉฐ, ์๋ฒ ๋ฐ์ดํฐ์ ๊ด๊ณ์๋ ๋ณด์ฌ์ฃผ๊ธฐ์ฉ ๊ฐ์ง ๋ฐ์ดํฐ๋ค.
- placeholderData์ ํจ์๋ฅผ ์ ๊ณตํ๋ ๊ฒฝ์ฐ ์ฒซ ๋ฒ์งธ ์ธ์๋ก ์ด์ ์ ๊ด์ฐฐ๋ ์ฟผ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๊ณ , ๋ ๋ฒ์งธ ์ธ์๋ ์ด์ ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ ๋๋ค.
### keepPreviousData
- v4๊น์ง ์๋ keepPreviousData์ `ํ์ด์ง๋ค์ด์ `๊ณผ ๊ฐ์ ๊ธฐ๋ฅ์ ๊ตฌํํ ๋ ๋ง์ด ์ฌ์ฉํ๋ ์ต์ ์ด์๋ค. ์บ์ฑ ๋์ง ์์ ํ์ด์ง๋ฅผ ๊ฐ์ ธ์ฌ ๋ ๋ชฉ๋ก์ด `๊น๋นก๊ฑฐ๋ฆฌ๋ ํ์์ ๋ฐฉ์ง`ํ ์ ์๋ค.
- **ํ์ง๋ง, v5๋ถํฐ `keepPreviousData`, `isPreviousData`์ ์ต์ ์ ์ ๊ฑฐ๋๋ค.**- [Removed keepPreviousData in favor of placeholderData identity function](https://github.com/ssi02014/react-query-tutorial/blob/main/document/v5.md#9-%EF%B8%8F-removed-keeppreviousdata-in-favor-of-placeholderdata-identity-function)
- ์ด๋ค์ ๊ฐ๊ฐ `placeholderData`์ `isPlaceholderData` ํ๋๊ทธ์ ๊ฑฐ์ ์ ์ฌํ๊ฒ ๋์ํ๊ธฐ ๋๋ฌธ์ด๋ค.
- ์๋ ์์ ์ฒ๋ผ `placeholderData`๋ฅผ ํ์ฉํ๋ฉด์ ์ด์ ๋ฒ์ ์์ `keepPreviousData์ ๊ฐ์ "true"`๋ก ์คฌ์ ๋์ ๋์ผํ ๊ธฐ๋ฅ์ ์ํํ ์ ์๋ค.```tsx
import { useQuery, keepPreviousData } from "@tanstack/react-query";const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
placeholderData: keepPreviousData,
});
```- ์๋ ์์์ฒ๋ผ ์์ฑํด์ ์์ `keepPreviousData` ์์์ ๋์ผํ ๋์์ ํ ์ ์๋ค.
```tsx
import { useQuery } from "@tanstack/react-query";const {
data,
// ...
} = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
placeholderData: (previousData, previousQuery) => previousData,
});
```
### notifyOnChangeProps
```tsx
import { useQuery } from "@tanstack/react-query";const { data, dataUpdatedAt } = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
notifyOnChangeProps: ["data"], // data ๊ฐ ๋ณ๊ฒฝ์์๋ง ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ๋ค
});
```- notifyOnChangeProps: `string[] | "all" | (() => string[] | "all")`
- ์ฟผ๋ฆฌ์ ํน์ ํ๋กํผํฐ๋ค์ด ๋ณ๊ฒฝ๋์์ ๋๋ง ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ๋๋ก ์ค์ ํ ์ ์๋ค.
- ๋ณ๋๋ก ์ค์ ํ์ง ์์ผ๋ฉด, **์ปดํฌ๋ํธ์์ ์ ๊ทผํ ๊ฐ์ด ๋ณ๊ฒฝ๋์์ ๋** ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ๋ค (๊ธฐ๋ณธ ๋์). ์ฆ, ์ ์์์์ `notifyOnChangeProps`์ ์ค์ ๊ฐ์ ์ฃผ์ง ์์๋ค๋ฉด, `data`, `dataUpdatedAt` ์ค ์ด๋ ํ๋๊ฐ ๋ณ๊ฒฝ๋๋ฉด ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ๋ค.
- `"all"`๋ก ์ค์ ํ ๊ฒฝ์ฐ, ์ฟผ๋ฆฌ์ ์ด๋ค ํ๋กํผํฐ๊ฐ ๋ณ๊ฒฝ๋๋ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง๋๋ค.
- ์ฐธ๊ณ : ๊ธฐ๋ณธ ๋์์ [Object.defineProperty()](https://github.com/TanStack/query/pull/1578/files#diff-93f379800fc8abf895eba249b2e2371eda98740aa40fc9f284a8088d190f46c3R506-R514)๋ฅผ ํ์ฉํ๋ค.
## Parallel
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- [useQueries ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/reference/useQueries)
```tsx
const { data: superHeroes } = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
});const { data: friends } = useQuery({
queryKey: ["friends"],
queryFn: getFriends,
});
```- ๋ช ๊ฐ์ง ์ํฉ์ ์ ์ธํ๋ฉด ์ฟผ๋ฆฌ ์ฌ๋ฌ ๊ฐ๊ฐ ์ ์ธ๋ ์ผ๋ฐ์ ์ธ ์ํฉ์ผ ๋, ์ฟผ๋ฆฌ ํจ์๋ค์ `๊ทธ๋ฅ ๋ณ๋ ฌ๋ก ์์ฒญ๋ผ์ ์ฒ๋ฆฌ`๋๋ค.
- ์ด๋ฌํ ํน์ง์ ์ฟผ๋ฆฌ ์ฒ๋ฆฌ์ `๋์์ฑ`์ ๊ทน๋ํ์ํจ๋ค.```tsx
const queryResults = useQueries({
queries: [
{
queryKey: ["super-hero", 1],
queryFn: () => getSuperHero(1),
staleTime: Infinity, // ๋ค์๊ณผ ๊ฐ์ด option ์ถ๊ฐ ๊ฐ๋ฅ!
},
{
queryKey: ["super-hero", 2],
queryFn: () => getSuperHero(2),
staleTime: 0,
},
// ...
],
});
```- ํ์ง๋ง, ์ฟผ๋ฆฌ ์ฌ๋ฌ ๊ฐ๋ฅผ ๋์์ ์ํํด์ผ ํ๋๋ฐ, ๋ ๋๋ง์ด ๊ฑฐ๋ญ๋๋ ์ฌ์ด์ฌ์ด์ ๊ณ์ ์ฟผ๋ฆฌ๊ฐ ์ํ๋์ด์ผ ํ๋ค๋ฉด ์ฟผ๋ฆฌ๋ฅผ ์ํํ๋ ๋ก์ง์ด hook ๊ท์น์ ์ด๊ธ๋ ์๋ ์๋ค. ์ด๋ด ๋๋ `useQueries`๋ฅผ ์ฌ์ฉํ๋ค.
- useQueries ํ ์ ๋ชจ๋ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ ํฌํจ๋ ๋ฐฐ์ด์ ๋ฐํํ๋ค. ๋ฐํ๋๋ ์์๋ ์ฟผ๋ฆฌ๊ฐ ์ ๋ ฅ๋ ์์์ ๋์ผํ๋ค.### Queries Combine
- [useQueries Combine ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/reference/useQueries#combine)
- useQueries ํ ์ด ๋ฐํํ ๋ชจ๋ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๊ฐ ํฌํจ๋ ๋ฐฐ์ด์ ๋จ์ผ ๊ฐ์ผ๋ก ๊ฒฐํฉํ๋ ค๋ฉด combine ์ต์ ์ ์ฌ์ฉํ ์ ์๋ค.
```tsx
const ids = [1,2,3]
const combinedQueries = useQueries({
queries: ids.map(id => (
{ queryKey: ["post", id], queryFn: () => fetchPost(id) },
)),
combine: (results) => {
return ({
data: results.map(result => result.data),
pending: results.some(result => result.isPending),
})
}
})
```- combinedQueries๋ `data`์ `pending` ํ๋กํผํฐ๋ฅผ ๊ฐ๋๋ค.
- _Note_: ์ฐธ๊ณ ๋ก ๊ฒฐํฉํ๋ฉด ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์ ๋๋จธ์ง ๋ค๋ฅธ ํ๋กํผํฐ๋ค์ ์์ค๋๋ค.
## Dependent Queries
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- [Dependent Queries ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/guides/dependent-queries)
- `์ข ์ ์ฟผ๋ฆฌ`๋ ์ด๋ค A๋ผ๋ ์ฟผ๋ฆฌ๊ฐ ์๋๋ฐ ์ด A ์ฟผ๋ฆฌ๋ฅผ ์คํํ๊ธฐ ์ ์ ์ฌ์ ์ ์๋ฃ๋์ด์ผ ํ๋ B ์ฟผ๋ฆฌ๊ฐ ์๋๋ฐ, ์ด๋ฌํ B ์ฟผ๋ฆฌ์ ์์กดํ๋ A ์ฟผ๋ฆฌ๋ฅผ ์ข ์ ์ฟผ๋ฆฌ๋ผ๊ณ ํ๋ค.
- react-query์์๋ `enabled` ์ต์ ์ ํตํด ์ข ์ ์ฟผ๋ฆฌ๋ฅผ ์ฝ๊ฒ ๊ตฌํํ ์ ์๋ค.```tsx
// ์ฌ์ ์ ์๋ฃ๋์ด์ผ ํ ์ฟผ๋ฆฌ
const { data: user } = useQuery({
queryKey: ["user", email],
queryFn: () => getUserByEmail(email),
});const channelId = user?.data.channelId;
// user ์ฟผ๋ฆฌ์ ์ข ์ ์ฟผ๋ฆฌ
const { data: courses } = useQuery({
queryKey: ["courses", channelId],
queryFn: () => getCoursesByChannelId(channelId),
enabled: !!channelId,
});
```
## useQueryClient
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- useQueryClient๋ `QueryClient` ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ค.
- `QueryClient`๋ ์บ์์ ์ํธ์์ฉํ๋ค.
- QueryClient๋ ๋ค์ ๋ฌธ์์์ ์์ธํ๊ฒ ๋ค๋ฃฌ๋ค.
- [QueryClient](https://github.com/ssi02014/react-query-tutorial/tree/master/document/queryClient.md)```tsx
import { useQueryClient } from "@tanstack/react-query";const queryClient = useQueryClient();
```
## Initial Query Data
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- [Initial Query Data ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/guides/initial-query-data)
- ์ฟผ๋ฆฌ์ ๋ํ `์ด๊ธฐ ๋ฐ์ดํฐ`๊ฐ ํ์ํ๊ธฐ ์ ์ ์บ์์ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ์ด ์๋ค.
- initialData ์ต์ ์ ํตํด์ ์ฟผ๋ฆฌ๋ฅผ ๋ฏธ๋ฆฌ ์ฑ์ฐ๋ ๋ฐ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ์ด๊ธฐ ๋ก๋ ์ํ๋ ๊ฑด๋๋ธ ์๋ ์๋ค.```tsx
const useSuperHeroData = (heroId: string) => {
const queryClient = useQueryClient();return useQuery({
queryKey: ["super-hero", heroId],
queryFn: () => getSuperHero(heroId),
initialData: () => {
const queryData = queryClient.getQueryData(["super-heroes"]) as any;
const hero = queryData?.data?.find(
(hero: Hero) => hero.id === parseInt(heroId)
);if (hero) return { data: hero };
},
});
};
```
- ์ฐธ๊ณ ๋ก ์ ์์ ์์ `queryClient.getQueryData` ๋ฉ์๋๋ ๊ธฐ์กด ์ฟผ๋ฆฌ์ `์บ์ฑ ๋ ๋ฐ์ดํฐ`๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ ๋๊ธฐ ํจ์์ด๋ค. ์ฟผ๋ฆฌ๊ฐ ์กด์ฌํ์ง ์์ผ๋ฉด `undefined`๋ฅผ ๋ฐํํ๋ค.
## Prefetching
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- [prefetching ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/guides/prefetching)
- prefetch๋ ๋ง ๊ทธ๋๋ก ๋ฏธ๋ฆฌ fetchํด์ค๊ฒ ๋ค๋ ์๋ฏธ์ด๋ค.
- ๋น๋๊ธฐ ์์ฒญ์ ๋ฐ์ดํฐ์์ด ํด์๋ก ๋ฐ์์ค๋ ์๋๊ฐ ๋๋ฆฌ๊ณ , ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฐ๋ค. ์ฌ์ฉ์ ๊ฒฝํ์ ์ํด ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ฐ์์์ ์บ์ฑํด ๋์ผ๋ฉด? ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๋ฐ๊ธฐ ์ ์ ์ฌ์ฉ์๊ฐ ์บ์ฑ ๋ ๋ฐ์ดํฐ๋ฅผ ๋ณผ ์ ์์ด `UX์ ์ข์ ์ํฅ`์ ์ค ์ ์๋ค.
- ์๋ฅผ ๋ค์ด ํ์ด์ง๋ค์ด์ ์ ๊ตฌํํ๋ค๊ณ ๊ฐ์ ํ๋ฉด, ํ์ด์ง1์์ ํ์ด์ง2๋ก ์ด๋ํ์ ๋ ํ์ด์ง3์ ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ฐ์๋๋ ๊ฒ์ด๋ค!
- react query์์๋ `queryClient.prefetchQuery`์ ํตํด์ prefetch ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.### prefetchQuery
```tsx
const prefetchNextPosts = async (nextPage: number) => {
const queryClient = useQueryClient();
// ํด๋น ์ฟผ๋ฆฌ์ ๊ฒฐ๊ณผ๋ ์ผ๋ฐ ์ฟผ๋ฆฌ๋ค์ฒ๋ผ ์บ์ฑ ๋๋ค.
await queryClient.prefetchQuery({
queryKey: ["posts", nextPage],
queryFn: () => fetchPosts(nextPage),
// ...options
});
};// ๋จ์ ์
useEffect(() => {
const nextPage = currentPage + 1;if (nextPage < maxPage) {
prefetchNextPosts(nextPage);
}
}, [currentPage]);
```- ์ฐธ๊ณ ๋ก prefetchQuery๋ฅผ ํตํด ๊ฐ์ ธ์ค๋ ์ฟผ๋ฆฌ์ ๋ํ ๋ฐ์ดํฐ๊ฐ `์ด๋ฏธ ์บ์ฑ ๋์ด ์์ผ๋ฉด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค์ง ์๋๋ค.`
### prefetchInfiniteQuery
- infinite ์ฟผ๋ฆฌ๋ ๋ฐ๋ก ์๋์ ๋์ค๊ฒ ์ง๋ง ์ผ๋ฐ ์ฟผ๋ฆฌ๋ค์ฒ๋ผ infinite ์ฟผ๋ฆฌ๋ prefetch ํ ์ ์๋ค.
- ๊ธฐ๋ณธ์ ์ผ๋ก ์ฟผ๋ฆฌ์ `์ฒซ ๋ฒ์งธ ํ์ด์ง๋ง prefetch` ๋๋ฉฐ, ๊ทธ ์ด์์ prefetch ํ๋ ค๋ฉด `pages ์ต์ `์ ํ์ฉํด์ผ ํ๋ค.
- ์ด ๊ฒฝ์ฐ์๋ `getNextPageParam` ํจ์๋ฅผ ๋ฌด์กฐ๊ฑด ์ ๊ณตํด ์ค์ผ ํ๋ค๋ ์ ์ ์ฃผ์ํ์.```tsx
const prefetchTodos = async () => {
await queryClient.prefetchInfiniteQuery({
queryKey: ["projects"],
queryFn: fetchProjects,
initialPageParam: 0,
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
pages: 3, // prefetch the first 3 pages
});
};
```
## Infinite Queries
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- [Infinite Queries ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/guides/infinite-queries)
- [useInfiniteQuery ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/reference/useInfiniteQuery)
- Infinite Queries(๋ฌดํ ์ฟผ๋ฆฌ)๋ `๋ฌดํ ์คํฌ๋กค`์ด๋ `load more(๋ ๋ณด๊ธฐ)`๊ณผ ๊ฐ์ด ํน์ ์กฐ๊ฑด์์ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐ์ ์ผ๋ก ๋ฐ์์ค๋ ๊ธฐ๋ฅ์ ๊ตฌํํ ๋ ์ฌ์ฉํ๋ฉด ์ ์ฉํ๋ค.
- react-query๋ ์ด๋ฌํ ๋ฌดํ ์ฟผ๋ฆฌ๋ฅผ ์ง์ํ๊ธฐ ์ํด useQuery์ ์ ์ฉํ ๋ฒ์ ์ธ `useInfiniteQuery`์ ์ง์ํ๋ค.```tsx
import { useInfiniteQuery } from "@tanstack/react-query";// useInfiniteQuery์ queryFn์ ๋งค๊ฐ๋ณ์๋ `pageParam`์ด๋ผ๋ ํ๋กํผํฐ๋ฅผ ๊ฐ์ง ์ ์๋ค.
const fetchColors = async ({
pageParam,
}: {
pageParam: number;
}): Promise> => {
return await axios.get(`http://localhost:4000/colors?page=${pageParam}`);
};const InfiniteQueries = () => {
const { data, hasNextPage, isFetching, isFetchingNextPage, fetchNextPage } =
useInfiniteQuery({
queryKey: ["colors"],
queryFn: fetchColors,
initialPageParam: 1,
getNextPageParam: (lastPage, allPages) => {
return allPages.length < 4 && allPages.length + 1;
},
// ...
});return (
{data?.pages.map((group, idx) => ({
/* ... */
}))}
fetchNextPage()}>
LoadMore
{isFetching && !isFetchingNextPage ? "Fetching..." : null}
);
};
```### ์ฃผ์ ๋ฐํ
- `useInfiniteQuery`๋ ๊ธฐ๋ณธ์ ์ผ๋ก useQuery์ ์ฌ์ฉ๋ฒ์ ๋น์ทํ์ง๋ง, ์ฐจ์ด์ ์ด ์๋ค.
- useInfiniteQuery๋ ๋ฐํ ๊ฐ์ผ๋ก `isFetchingNextPage`, `isFetchingPreviousPage`, `fetchNextPage`, `fetchPreviousPage`, `hasNextPage` ๋ฑ์ด ์ถ๊ฐ์ ์ผ๋ก ์๋ค.
- data.pages: ๋ชจ๋ ํ์ด์ง ๋ฐ์ดํฐ๋ฅผ ํฌํจํ๋ ๋ฐฐ์ด์ด๋ค.
- data.pageParams: ๋ชจ๋ ํ์ด์ง ๋งค๊ฐ๋ณ์๋ฅผ ํฌํจํ๋ ๋ฐฐ์ด์ด๋ค.
- fetchNextPage: `๋ค์ ํ์ด์ง`๋ฅผ fetch ํ ์ ์๋ค.
- fetchPreviousPage: `์ด์ ํ์ด์ง`๋ฅผ fetch ํ ์ ์๋ค.
- isFetchingNextPage: `fetchNextPage` ๋ฉ์๋๊ฐ ๋ค์ ํ์ด์ง๋ฅผ ๊ฐ์ ธ์ค๋ ๋์ true์ด๋ค.
- isFetchingPreviousPage: `fetchPreviousPage` ๋ฉ์๋๊ฐ ์ด์ ํ์ด์ง๋ฅผ ๊ฐ์ ธ์ค๋ ๋์ true์ด๋ค.
- hasNextPage: ๊ฐ์ ธ์ฌ ์ ์๋ `๋ค์ ํ์ด์ง`๊ฐ ์์ ๊ฒฝ์ฐ true์ด๋ค.
- hasPreviousPage: ๊ฐ์ ธ์ฌ ์ ์๋ `์ด์ ํ์ด์ง`๊ฐ ์์ ๊ฒฝ์ฐ true์ด๋ค.
### ์ฃผ์ ์ต์
1. initialPageParam: `TPageParam`
- initialPageParam์ ์ด์ฉํด์ ์ฒซ ํ์ด์ง๋ฅผ ๊ฐ์ ธ์ฌ ๋ ์ฌ์ฉํ ๊ธฐ๋ณธ ํ์ด์ง ๋งค๊ฐ๋ณ์์ด๋ค. `ํ์๊ฐ`์ด๋ค.
2. getNextPageParam: `(lastPage, allPages, lastPageParam, allPageParams) => TPageParam | undefined | null`
- getNextPageParam ์ ์ด์ฉํด์ ํ์ด์ง๋ฅผ ์ฆ๊ฐ์ํฌ ์ ์๋ค. `ํ์๊ฐ`์ด๋ค.
- getNextPageParam์ ์ฒซ ๋ฒ์งธ ์ธ์ `lastPage`๋ fetch ํด์จ ๊ฐ์ฅ ์ต๊ทผ์ ๊ฐ์ ธ์จ ํ์ด์ง ๋ชฉ๋ก์ด๋ค.
- ๋ ๋ฒ์งธ ์ธ์ `allPages`๋ ํ์ฌ๊น์ง ๊ฐ์ ธ์จ ๋ชจ๋ ํ์ด์ง ๋ฐ์ดํฐ์ด๋ค.
- ์ธ ๋ฒ์งธ ์ธ์ `firstPageParam` ๋ ์ฒซ ๋ฒ์งธ ํ์ด์ง์ ๋งค๊ฐ๋ณ์์ด๋ค.
- ๋ค ๋ฒ์งธ ์ธ์ `allPageParams` ๋ ๋ชจ๋ ํ์ด์ง์ ๋งค๊ฐ๋ณ์์ด๋ค.
- ์ฌ์ฉ ๊ฐ๋ฅํ ๋ค์ ํ์ด์ง๊ฐ ์์์ ํ์ํ๋ ค๋ฉด `undefined` ๋๋ `null`์ ๋ฐํํ๋ฉด ๋๋ค.
- `getPreviousPageParam`๋ ์กด์ฌํ๋ฉฐ, `getNextPageParam`์ ๋ฐ๋์ ์์ฑ์ ๊ฐ๊ณ ์๋ค.3. maxPages: `number | undefined`
- infinite ์ฟผ๋ฆฌ์ ์ ์ฅํ `์ต๋ ํ์ด์ง ์`์ด๋ค.
- ์ต๋ ํ์ด์ง ์์ ๋๋ฌํ๋๋ฐ ์ ํ์ด์ง๋ฅผ ๊ฐ์ ธ์ค๋ฉด `์ง์ ๋ ๋ฐฉํฅ(next, previous)`์ ๋ฐ๋ผ ํ์ด์ง ๋ฐฐ์ด์์ ์ฒซ ๋ฒ์งธ ํ์ด์ง ๋๋ ๋ง์ง๋ง ํ์ด์ง๊ฐ ์ ๊ฑฐ๋๋ค.
- `0` ๋๋ `undefined`๋ผ๋ฉด ํ์ด์ง ์๋ ๋ฌด์ ํ์ด๋ค.
### ๐ก pageParam
- `queryFn`์ ๋๊ฒจ์ฃผ๋ pageParam๊ฐ ๋จ์ํ ๋ค์ page์ ๊ฐ๋ง์ ๊ด๋ฆฌํ ์ ์๋ ๊ฒ์ ์๋๋ค.
- pageParam ๊ฐ์ `getNextPageParam`์์ ์ํ๋ ํํ๋ก ๋ณ๊ฒฝ์์ผ ์ค ์ ์๋ค.
- ๋ฌด์จ ๋ง์ธ์ง ์์๋ฅผ ๋ณด๋ฉด ์ดํด๊ฐ ์ฝ๋ค. ๐ ์๋์ ๊ฐ์ด getNextPageParam์์ ๋ฐํ ๋ฐ์ดํฐ๊ฐ ๋จ์ํ ๋ค์ ํ์ด์ง๊ฐ์ด ์๋ ๊ฐ์ฒด๋ก ๋ฐํํ๋ค๊ณ ํด๋ณด์.```tsx
const { data, hasNextPage, isFetching, isFetchingNextPage, fetchNextPage } =
useInfiniteQuery({
queryKey: ["colors"],
queryFn: ({ pageParam }) => fetchColors(pageParam), // pageParam: { page: number; etc: string }
initialPageParam: {
page: number,
etc: "hi",
},
getNextPageParam: (lastPage, allPages) => {
return (
allPages.length < 4 && {
page: allPages.length + 1,
etc: "bye",
};
)
},
});
```- ๊ทธ๋ฌ๋ฉด `queryFn`์ ๋ฃ์ pageParam์์ getNextPageParam์์ ๋ฐํํ ๊ฐ์ฒด๋ฅผ ๋ฐ์์ฌ ์ ์๋ค.
```tsx
const fetchColors = async ({
page,
etc,
}: {
page: number;
etc: string;
}): Promise> => {
return await axios.get(
`http://localhost:4000/colors?page=${page}?etc=${etc}`
);
};
```- ์ฆ, getNextPageParam์ ๋ฐํ ๊ฐ์ด pageParams๋ก ๋ค์ด๊ฐ๊ธฐ ๋๋ฌธ์ pageParams๋ฅผ ์ํ๋ ํํ๋ก ๋ณ๊ฒฝํ๊ณ ์ถ๋ค๋ฉด getNextPageParam์ ๋ฐํ ๊ฐ์ ์ค์ ํ๋ฉด ๋๋ค.
## useMutation
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- [useMutation ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/reference/useMutation)
- react-query์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ Get ํ ๋๋ useQuery๋ฅผ ์ฌ์ฉํ๋ค.
- ๋ง์ฝ ์๋ฒ์ data๋ฅผ post, patch, put, delete์ ๊ฐ์ด ์์ ํ๊ณ ์ ํ๋ค๋ฉด ์ด๋๋ useMutation์ ์ด์ฉํ๋ค.
- ์์ฝํ์๋ฉด `R(read)๋ useQuery`, `CUD(Create, Update, Delete)๋ useMutation`์ ์ฌ์ฉํ๋ค.```tsx
const mutation = useMutation({
mutationFn: createTodo,
onMutate() {
/* ... */
},
onSuccess(data) {
console.log(data);
},
onError(err) {
console.log(err);
},
onSettled() {
/* ... */
},
});const onCreateTodo = (e) => {
e.preventDefault();
mutate({ title });
};
```- useMutation์ ๋ฐํ ๊ฐ์ธ mutation ๊ฐ์ฒด์ `mutate` ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ์์ฒญ ํจ์๋ฅผ ํธ์ถํ ์ ์๋ค.
- mutate๋ `onSuccess`, `onError` ๋ฉ์๋๋ฅผ ํตํด ์ฑ๊ณตํ์ ์, ์คํจํ์ ์ response ๋ฐ์ดํฐ๋ฅผ ํธ๋ค๋งํ ์ ์๋ค.
- `onMutate`๋ mutation ํจ์๊ฐ ์คํ๋๊ธฐ ์ ์ ์คํ๋๊ณ , mutation ํจ์๊ฐ ๋ฐ์ ๋์ผํ ๋ณ์๊ฐ ์ ๋ฌ๋๋ค.
- `onSettled`๋ try...catch...finally ๊ตฌ๋ฌธ์ `finally`์ฒ๋ผ ์์ฒญ์ด ์ฑ๊ณตํ๋ ์๋ฌ๊ฐ ๋ฐ์ํ๋ ์๊ด์์ด ๋ง์ง๋ง์ ์คํ๋๋ค.```tsx
const mutation = useMutation(addTodo);try {
const todo = await mutation.mutateAsync(todo);
console.log(todo);
} catch (error) {
console.error(error);
} finally {
console.log("done");
}
```- ๋ง์ฝ, useMutation์ ์ฌ์ฉํ ๋ promise ํํ์ response๊ฐ ํ์ํ ๊ฒฝ์ฐ๋ผ๋ฉด `mutateAsync`๋ฅผ ์ฌ์ฉํด์ ์ป์ด์ฌ ์ ์๋ค.
### ๐ก mutate์ mutateAsync๋ ๋ฌด์์ ์ฌ์ฉํ๋ ๊ฒ ์ข์๊น?
- ๋๋ถ๋ถ์ ๊ฒฝ์ฐ์ ์ฐ๋ฆฌ๋ mutate๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ ๋ฆฌํ๋ค. ์๋ํ๋ฉด mutate๋ ์ฝ๋ฐฑ(onSuccess, onError)๋ฅผ ํตํด data์ error์ ์ ๊ทผํ ์ ์๊ธฐ ๋๋ฌธ์ ์ฐ๋ฆฌ๊ฐ ํน๋ณํ ํธ๋ค๋งํด ์ค ํ์๊ฐ ์๋ค.
- ํ์ง๋ง mutateAsync๋ Promise๋ฅผ ์ง์ ๋ค๋ฃจ๊ธฐ ๋๋ฌธ์ ์ด๋ฐ ์๋ฌ ํธ๋ค๋ง ๊ฐ์ ๋ถ๋ถ์ ์ง์ ๋ค๋ค์ผ ํ๋ค.
- ๋ง์ฝ ์ด๋ฅผ ๋ค๋ฃจ์ง ์์ผ๋ฉด `unhandled promise rejection` ์๋ฌ๊ฐ ๋ฐ์ํ ์ ์๋ค.
- [TkDodo Blog: Mutate or MutateAsync](https://tkdodo.eu/blog/mastering-mutations-in-react-query#mutate-or-mutateasync)
### ๐ก useMutation callback๊ณผ mutate callback์ ์ฐจ์ด
- useMutation์ onSuccess, onError, onSettled์ ๊ฐ์ Callback ํจ์๋ค์ ๊ฐ์ง ์ ์๋ค.
- ๊ทธ๋ฟ๋ง ์๋๋ผ, mutate ์ญ์ ์์ ๊ฐ์ Callback ํจ์๋ค์ ๊ฐ์ง ์ ์๋ค.
- ๋์ ๋์์ ๊ฐ๋ค๊ณ ์๊ฐํ ์ ์์ง๋ง ์ฝ๊ฐ์ ์ฐจ์ด๊ฐ ์๋ค. ๋ค์๊ณผ ๊ฐ๋ค.
- useMutation์ Callback ํจ์์ mutate์ Callback ํจ์๋ ๋ ๋ฆฝ์ ์ผ๋ก ์คํ๋๋ค.
- ์์๋ `useMutation์ Callback -> mutate์ Callback` ์์ผ๋ก ์คํ๋๋ค.
- mutation์ด ์๋ฃ๋๊ธฐ ์ ์ ์ปดํฌ๋ํธ๊ฐ unmount๋๋ค๋ฉด mutate์ Callback์ ์คํ๋์ง ์์ ์ ์๋ค.
- `TkDodo`๋ ์์ ๊ฐ์ ์ด์ ๋ก ๋์ ๋ถ๋ฆฌํด์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ ์ ํ๋ค๊ณ ํ๋ค.
- ๊ผญ ํ์ํ ๋ก์ง(ex. `์ฟผ๋ฆฌ ์ด๊ธฐํ`)์ useMutation์ Callback์ผ๋ก ์คํ์ํจ๋ค.
- ๋ฆฌ๋ค์ด๋ ์ ๋ฐ UI ๊ด๋ จ ์์ ์ mutate Callback์์ ์คํ์ํจ๋ค.
- [TkDodo Blog: Some callbacks might not fire](https://tkdodo.eu/blog/mastering-mutations-in-react-query#some-callbacks-might-not-fire)
## cancelQueries
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- [Query Cancellation ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/guides/query-cancellation)
- ์ฟผ๋ฆฌ๋ฅผ `์๋์ผ๋ก ์ทจ์`ํ๊ณ ์ถ์ ์๋ ์๋ค.
- ์๋ฅผ ๋ค์ด ์์ฒญ์ ์๋ฃํ๋ ๋ฐ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ๊ฒฝ์ฐ ์ฌ์ฉ์๊ฐ ์ทจ์ ๋ฒํผ์ ํด๋ฆญํ์ฌ ์์ฒญ์ ์ค์งํ๋๋ก ํ์ฉํ ์ ์๋ค.
- ๋๋, ์์ง HTTP ์์ฒญ์ด ๋๋์ง ์์์ ๋, ํ์ด์ง๋ฅผ ๋ฒ์ด๋ ๋๋ ์ค๊ฐ์ ์ทจ์ํด์ ๋ถํ์ํ ๋คํธ์ํฌ ๋ฆฌ์์ค๋ฅผ ๊ฐ์ ํ ์ ์๋ค.
- ์ด๋ ๊ฒ ํ๋ ค๋ฉด ์ฟผ๋ฆฌ๋ฅผ ์ทจ์ํ๊ณ ์ด์ ์ํ๋ก ๋๋๋ฆฌ๊ธฐ ์ํด `queryClient.cancelQueries(queryKey)`๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ๋ํ react-query๋ ์ฟผ๋ฆฌ ์ทจ์๋ฟ๋ง์๋๋ผ queryFn์ Promise๋ ์ทจ์ํ๋ค.```tsx
const query = useQuery({
queryKey: ["super-heroes"],
queryFn: getAllSuperHero,
});const queryClient = useQueryClient();
const onCancelQuery = (e) => {
e.preventDefault();queryClient.cancelQueries({
queryKey: ["super-heroes"],
});
};return Cancel;
```
## ์ฟผ๋ฆฌ ๋ฌดํจํ
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- invalidateQueries์ ํ๋ฉด์ ์ต์ ์ํ๋ก ์ ์งํ๋ ๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ด๋ค.
- ์๋ฅผ ๋ค๋ฉด, ๊ฒ์ํ ๋ชฉ๋ก์์ ์ด๋ค ๊ฒ์๊ธ์ `์์ฑ(Post)`ํ๊ฑฐ๋ ๊ฒ์๊ธ์ `์ ๊ฑฐ(Delete)`ํ์ ๋ ํ๋ฉด์ ๋ณด์ฌ์ฃผ๋ ๊ฒ์ํ ๋ชฉ๋ก์ ์ค์๊ฐ์ผ๋ก ์ต์ ํํด์ผ ํ ๋๊ฐ ์๋ค.
- ํ์ง๋ง ์ด๋, `query Key`๊ฐ ๋ณํ์ง ์์ผ๋ฏ๋ก ๊ฐ์ ๋ก ์ฟผ๋ฆฌ๋ฅผ ๋ฌดํจํํ๊ณ ์ต์ ํ๋ฅผ ์งํํด์ผ ํ๋๋ฐ, ์ด๋ฐ ๊ฒฝ์ฐ์ `invalidateQueries()` ๋ฉ์๋๋ฅผ ์ด์ฉํ ์ ์๋ค.
- ์ฆ, query๊ฐ ์ค๋๋์๋ค๋ ๊ฒ์ ํ๋จํ๊ณ ๋ค์ `refetch`๋ฅผ ํ ๋ ์ฌ์ฉํ๋ค!```tsx
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";const useAddSuperHeroData = () => {
const queryClient = useQueryClient();return useMutation(addSuperHero, {
onSuccess(data) {
queryClient.invalidateQueries({ queryKey: ["super-heroes"] }); // ์ด key์ ํด๋นํ๋ ์ฟผ๋ฆฌ๊ฐ ๋ฌดํจํ!
console.log(data);
},
onError(err) {
console.log(err);
},
});
};
```- ์ฐธ๊ณ ๋ก, queryKey์ `["super-heroes"]`์ ๋๊ฒจ์ฃผ๋ฉด queryKey์ "super-heroes"๋ฅผ ํฌํจํ๋ ๋ชจ๋ ์ฟผ๋ฆฌ๊ฐ ๋ฌดํจํ๋๋ค.
```tsx
queryClient.invalidateQueries({ queryKey: ["super-heroes"] });// ์๋ query๋ค ๋ชจ๋ ๋ฌดํจํ ๋๋ค.
const query = useQuery({
queryKey: ["super-heroes", "superman"],
queryFn: fetchSuperHero,
});
const query = useQuery({
queryKey: ["super-heroes", { id: 1 }],
queryFn: fetchSuperHero,
});
```- ์์ `enabled/refetch`์์๋ ์ธ๊ธํ์ง๋ง `enabled: false` ์ต์ ์ ์ฃผ๋ฉด`queryClient`๊ฐ ์ฟผ๋ฆฌ๋ฅผ ๋ค์ ๊ฐ์ ธ์ค๋ ๋ฐฉ๋ฒ ์ค `invalidateQueries`์ `refetchQueries`๋ฅผ ๋ฌด์ํ๋ค.
- [Disabling/Pausing Queries ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/guides/disabling-queries) ์ฐธ๊ณ
- ์์ธํ ๋ด์ฉ์ [queryClient.invalidateQueries ์ ๋ฆฌ](https://github.com/ssi02014/react-query-tutorial/blob/master/document/queryClient.md#invalidateQueries)๋ฅผ ์ฐธ๊ณ ํ์.
## ์บ์ ๋ฐ์ดํฐ ์ฆ์ ์ ๋ฐ์ดํธ
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- [queryClient.setQueryData ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclientsetquerydata)
- ๋ฐ๋ก ์์์ `queryClient.invalidateQueries`๋ฅผ ์ด์ฉํด ์บ์ ๋ฐ์ดํฐ๋ฅผ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ์์๋ดค๋๋ฐ `queryClient.setQueryData`๋ฅผ ์ด์ฉํด์๋ ๋ฐ์ดํฐ๋ฅผ ์ฆ์ ์ ๋ฐ์ดํธํ ์ ์๋ค.
- `queryClient.setQueryData`๋ ์ฟผ๋ฆฌ์ ์บ์ ๋ ๋ฐ์ดํฐ๋ฅผ ์ฆ์ ์ ๋ฐ์ดํธํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ `๋๊ธฐ ํจ์`์ด๋ค.
- setQueryData์ ๋ ๋ฒ์งธ ์ธ์๋ `updater` ํจ์์ด๋ค. ํด๋น ํจ์์ ์ฒซ ๋ฒ์งธ ๋งค๊ฐ๋ณ์๋ `oldData`๋ก ๊ธฐ์กด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ๋ค.```tsx
const useAddSuperHeroData = () => {
const queryClient = useQueryClient();return useMutation({
mutationFn: addSuperHero,
onSuccess(data) {
queryClient.setQueryData(["super-heroes"], (oldData: any) => {
return {
...oldData,
data: [...oldData.data, data.data],
};
});
},
onError(err) {
console.log(err);
},
});
};
```
## Optimistic Update
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- `Optimistic Update(๋๊ด์ ์ ๋ฐ์ดํธ)`๋ ์๋ฒ ์ ๋ฐ์ดํธ ์ UI์์๋ ์ด์ฐจํผ ์ ๋ฐ์ดํธํ ๊ฒ์ด๋ผ๊ณ (๋๊ด์ ์ธ) ๊ฐ์ ํด์ `๋ฏธ๋ฆฌ UI๋ฅผ ์ ๋ฐ์ดํธ` ์์ผ์ฃผ๊ณ ์๋ฒ๋ฅผ ํตํด ๊ฒ์ฆ๋ฐ๊ณ ์ ๋ฐ์ดํธ ๋๋ ๋กค๋ฐฑํ๋ ๋ฐฉ์์ด๋ค.
- ์๋ฅผ ๋ค์ด facebook์ ์ข์์ ๋ฒํผ์ด ์๋๋ฐ ์ด๊ฒ์ ์ ์ ๊ฐ ๋๋ฅธ๋ค๋ฉด, ์ผ๋จ client ์ชฝ state๋ฅผ ๋จผ์ ์ ๋ฐ์ดํธํ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ง์ฝ์ ์คํจํ๋ค๋ฉด, ์์ state๋ก ๋์๊ฐ๊ณ ์ฑ๊ณตํ๋ฉด ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ค์ fetch ํด์ ์๋ฒ ๋ฐ์ดํฐ์ ํ์คํ ์ฐ๋์ ์งํํ๋ค.
- Optimistic Update๊ฐ ์ ๋ง ์ ์ฉํ ๋๋ ์ธํฐ๋ท ์๋๊ฐ ๋๋ฆฌ๊ฑฐ๋ ์๋ฒ๊ฐ ๋๋ฆด ๋์ด๋ค. ์ ์ ๊ฐ ํํ ์ก์ ์ ๊ธฐ๋ค๋ฆด ํ์ ์์ด ๋ฐ๋ก ์ ๋ฐ์ดํธ๋๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์ ๊ฒฝํ(UX) ์ธก๋ฉด์์ ์ข๋ค.```tsx
const useAddSuperHeroData = () => {
const queryClient = useQueryClient();
return useMutation({
mutateFn: addSuperHero,
onMutate: async (newHero: any) => {
await queryClient.cancelQueries(["super-heroes"]);// ์ด์ ๊ฐ
const previousHeroData = queryClient.getQueryData(["super-heroes"]);// ์๋ก์ด ๊ฐ์ผ๋ก ๋๊ด์ ์ ๋ฐ์ดํธ ์งํ
queryClient.setQueryData(["super-heroes"], (oldData: any) => {
return {
...oldData,
data: [
...oldData.data,
{ ...newHero, id: oldData?.data?.length + 1 },
],
};
});// ๊ฐ์ด ๋ค์ด์๋ context ๊ฐ์ฒด๋ฅผ ๋ฐํ
return { previousHeroData };
},
// mutation์ด ์คํจํ๋ฉด onMutate์์ ๋ฐํ๋ context๋ฅผ ์ฌ์ฉํ์ฌ ๋กค๋ฐฑ ์งํ
onError(error, hero, context: any) {
queryClient.setQueryData(["super-heroes"], context.previousHeroData);
},
// ์ค๋ฅ ๋๋ ์ฑ๊ณต ํ์๋ ํญ์ refetch
onSettled() {
queryClient.invalidateQueries(["super-heroes"]);
},
});
};
```- ์ฐธ๊ณ ๋ก ์ ์์ ์์ `cancelQueries`๋ ์ฟผ๋ฆฌ๋ฅผ `์๋์ผ๋ก ์ทจ์`์ํฌ ์ ์๋ค. ์ทจ์์ํฌ query์ queryKey๋ฅผ cancelQueries์ ์ธ์๋ก ๋ณด๋ด ์คํ์ํจ๋ค.
- ์๋ฅผ ๋ค์ด, ์์ฒญ์ ์๋ฃํ๋ ๋ฐ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ๊ฒฝ์ฐ, ์ฌ์ฉ์๊ฐ ์ทจ์ ๋ฒํผ์ ํด๋ฆญํ์ฌ ์์ฒญ์ ์ค์งํ๋ ๊ฒฝ์ฐ ์ด์ฉํ ์ ์๋ค.
## useQueryErrorResetBoundary
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- [useQueryErrorResetBoundary ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/reference/useQueryErrorResetBoundary)
- react-query์์ ErrorBoundary์ useQueryErrorResetBoundary๋ฅผ ๊ฒฐํฉํด `์ ์ธ์ `์ผ๋ก ์๋ฌ๊ฐ ๋ฐ์ํ์ ๋ Fallback UI๋ฅผ ๋ณด์ฌ์ค ์ ์๋ค.
- ErrorBoundary์ ๋ํ ์ค๋ช ์ ํด๋น ๋ฌธ์ ์ฐธ๊ณ [ErrorBoundary](https://github.com/ssi02014/react-query-tutorial/blob/master/document/errorBoundary.md)
- `useQueryErrorResetBoundary`๋ `ErrorBoundary`์ ํจ๊ป ์ฌ์ฉ๋๋๋ฐ ์ด๋, ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฆฌ์กํธ ๊ณต์ ๋ฌธ์์์ ๊ธฐ๋ณธ ์ฝ๋ ๋ฒ ์ด์ค๊ฐ ์ ๊ณต๋๊ธด ํ์ง๋ง ์ข ๋ ์ฝ๊ฒ ํ์ฉํ ์ ์๋ `react-error-boundary` ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์กด์ฌํ๊ณ , react-query ๊ณต์ ๋ฌธ์์์๋ ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ์ ์์๋ก ์ ๊ณตํด ์ฃผ๊ธฐ ๋๋ฌธ์ `react-error-boundary`๋ฅผ ์ค์นํด์ ์ฌ์ฉํด ๋ณด์.
```bash
$ npm i react-error-boundary
# or
$ pnpm add react-error-boundary
# or
$ yarn add react-error-boundary
# or
$ bun add react-error-boundary
```- ์ค์น ํ์ ์๋์ ๊ฐ์ QueryErrorBoundary๋ผ๋ ์ปดํฌ๋ํธ๋ฅผ ์์ฑํ๊ณ , ๊ทธ ๋ด๋ถ์ `useQueryErrorResetBoundary` ํ ์ ํธ์ถํด `reset` ํจ์๋ฅผ ๊ฐ์ ธ์จ๋ค.
- ์๋ ์ฝ๋ ๋ด์ฉ์ ๋จ์ํ๋ค.
- Error๊ฐ ๋ฐ์ํ๋ฉด ErrorBoundary์ `fallbackRender` prop์ผ๋ก ๋๊ธด ๋ด์ฉ์ด ๋ ๋๋ง ๋๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉด children ๋ด์ฉ์ด ๋ ๋๋ง ๋๋ค.
- ๋ํ, fallbackRender์ ๋ฃ์ด์ฃผ๋ ์ฝ๋ฐฑ ํจ์ ๋งค๊ฐ ๋ณ์๋ก `resetErrorBoundary`๋ฅผ ๊ตฌ์กฐ ๋ถํด ํ ๋น์ ํตํด ๊ฐ์ ธ์ฌ ์ ์๋๋ฐ, ์ด๋ฅผ ํตํด ๋ชจ๋ ์ฟผ๋ฆฌ ์๋ฌ๋ฅผ `์ด๊ธฐํ`ํ ์ ์๋ค. ์๋ ์ฝ๋ ๊ฐ์ ๊ฒฝ์ฐ์๋ button์ ํด๋ฆญํ๋ฉด ์๋ฌ๋ฅผ ์ด๊ธฐํํ๊ฒ๋ ์์ฑํ๋ค.```tsx
import { useQueryErrorResetBoundary } from "@tanstack/react-query"; // (*)
import { ErrorBoundary } from "react-error-boundary"; // (*)interface Props {
children: React.ReactNode;
}const QueryErrorBoundary = ({ children }: Props) => {
const { reset } = useQueryErrorResetBoundary(); // (*)return (
(
Error!!
resetErrorBoundary()}>Try again
)}
>
{children}
);
};export default QueryErrorBoundary;
```- ๊ทธ๋ฆฌ๊ณ App.js์๋ค QueryErrorBoundary ์ปดํฌ๋ํธ๋ฅผ ์ถ๊ฐํ๋ฉด ๋๋ค. ์ฌ๊ธฐ์ ์ฃผ์ํ ์ ์ queryClient ์ต์ ์๋ค `{ throwOnError: true }`๋ฅผ ์ถ๊ฐํด์ผ ํ๋ค๋ ์ ์ด๋ค. ๊ทธ๋์ผ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ ๋ `ErrorBoundary` ์ปดํฌ๋ํธ๊ฐ ๊ฐ์งํ ์ ์๋ค.
```tsx
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import QueryErrorBoundary from "./components/ErrorBoundary"; // (*)const queryClient = new QueryClient({
defaultOptions: {
queries: {
throwOnError: true, // (*) ์ฌ๊ธฐ์๋ ๊ธ๋ก๋ฒ๋ก ์ธํ ํ์ง๋ง, ๊ฐ๋ณ ์ฟผ๋ฆฌ๋ก ์ธํ ๊ฐ๋ฅ
},
},
});function App() {
return (
{/* ํ์ ์ปดํฌ๋ํธ๋ค */}
);
}
```
## Suspense
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- ErrorBoundary๋ ์๋ฌ๊ฐ ๋ฐ์ํ์ ๋ ๋ณด์ฌ์ฃผ๋ Fallback UI๋ฅผ `์ ์ธ์ `์ผ๋ก ์์ฑํ ์ ์๊ณ , ๋ฆฌ์กํธ ์ฟผ๋ฆฌ๋ `Suspense`์๋ ๊ฒฐํฉํด์ `์๋ฒ ํต์ ์ํ๊ฐ ๋ก๋ฉ ์ค`์ผ ๋ Fallback UI๋ฅผ ๋ณด์ฌ์ค ์ ์๊ฒ ์ ์ธ์ ์ผ๋ก ์์ฑํ ์ ์๋ค.
- ์ฐธ๊ณ ๋ก, Suspense ์ปดํฌ๋ํธ๋ ๋ฆฌ์กํธ v16๋ถํฐ ์ ๊ณต๋๋ `Component Lazy Loading`์ด๋ `Data Fetching` ๋ฑ์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํ ๋, ์๋ต์ ๊ธฐ๋ค๋ฆฌ๋ ๋์ Fallback UI(ex: Loader)๋ฅผ ๋ณด์ฌ์ฃผ๋ ๊ธฐ๋ฅ์ ํ๋ ์ปดํฌ๋ํธ๋ค.```tsx
import { Suspense } from "react";const queryClient = new QueryClient({
defaultOptions: {
queries: {
// suspense: true, - ๐ก v5๋ถํฐ Deprecated
// useQuery/useInfiniteQuery์ ๊ฐ์ ์ผ๋ฐ ํ ๋์ useSuspenseQuery/useSuspenseInfiniteQuery์ ๊ฐ์ suspense ํ ์ฌ์ฉ
throwOnError: true,
},
},
});function App() {
return (
}>{/* ํ์ ์ปดํฌ๋ํธ๋ค */}
;
);
}
```- ์ฝ๋๋ฅผ ๋ณด๋ฉด ์ฐ๋ฆฌ๋ ์๋ฒ ์ํ๊ฐ ๋ก๋ฉ์ผ ๋ Loader ์ปดํฌ๋ํธ๋ฅผ ๋ณด์ฌ์ฃผ๊ฒ ๋ค!๋ผ๊ณ ์ดํดํ ์ ์๋ค.
- Suspense์ปดํฌ๋ํธ ๋ด๋ถ์์ ์ด๋ค ๋ก์ง์ด ๋์ํ๋์ง ์ฐ๋ฆฌ๋ ์ ๊ฒฝ ์ฐ์ง ์์๋๋๋ค. ์ด์ฒ๋ผ `๋ด๋ถ ๋ณต์ก์ฑ์ ์ถ์ํ`ํ๋ ๊ฒ ๋ฐ๋ก `์ ์ธํ ์ปดํฌ๋ํธ`์ด๋ค.
- ์์ ๊ฐ์ด `react-query(useSuspenseQuery)`์ ๊ฒฐํฉํ `Suspense`๋ ์๋์ ๊ฐ์ ๊ณผ์ ์ผ๋ก ๋์ํ๋ค.```
1. Suspense mount
2. MainComponent mount
3. MainComponent์์ useSuspenseQuery ํ ์ ์ฌ์ฉํ์ฌ ๋น๋๊ธฐ ๋ฐ์ดํฐ ์์ฒญ
4. MainComponent unmount, fallback UI์ธ Loader mount
5. ๋น๋๊ธฐ ๋ฐ์ดํฐ ์์ฒญ์ด ์๋ฃ๋๋ฉด fallback UI์ธ Loader unmount
6. MainComponent mount
```
### ๐ก New hooks for suspense
- [new hooks for suspense](https://github.com/ssi02014/react-query-tutorial/blob/main/document/v5.md#21-%EF%B8%8F-new-hooks-for-suspense)
- v5์์๋ `data fetching`์ ๋ํ `suspense`๊ฐ ๋ง์นจ๋ด ์์ ํ๋์์ต๋๋ค.
- `useSuspenseQuery`, `useSuspenseInfiniteQuery`, `useSuspenseQueries` 3๊ฐ์ง ํ ์ด ์ถ๊ฐ๋์์ต๋๋ค.
- ๊ธฐ์กด์ `suspense ์ต์ `์ ์ ๊ฑฐ๋์์ต๋๋ค. ๋ฐ๋ผ์ Suspense๋ฅผ ์ ์ฉํ๋ ค๋ฉด ์ ํ ๋ค์ ํ์ฉํด์ผ ํฉ๋๋ค.
- ์ 3๊ฐ์ง ํ ์ ์ฌ์ฉํ๊ฒ ๋๋ฉด ํ์ ๋ ๋ฒจ์์ `data`๊ฐ `undefined` ์ํ๊ฐ ๋์ง ์์ต๋๋ค.```tsx
import { useQuery, useSuspenseQuery } from "@tanstack/react-query";const fetchGroups = async (): Promise<{ data: Group[] }> => {
const res = await axios.get("/groups");
return res;
};// as-is
const { data } = useQuery({
queryKey: ["groups"],
queryFn: fetchGroups,
select: (data) => data.data,
});// to-be
const { data } = useSuspenseQuery({
queryKey: ["groups"],
queryFn: fetchGroups,
select: (data) => data.data,
});
```
### ๐ก @suspensive/react-query
- TanStack Query(React) ๊ณต์ ๋ฌธ์์ `Community Resources`์์๋ Suspense๋ฅผ ๋ `ํ์ ์ธ์ดํ`ํ๊ฒ ์ ์ฌ์ฉํ๊ธฐ ์ํด [useSuspenseQuery](https://suspensive.org/ko/docs/react-query/useSuspenseQuery), [useSuspenseQueries](https://suspensive.org/ko/docs/react-query/useSuspenseQueries), [useSuspenseInfiniteQuery](https://suspensive.org/ko/docs/react-query/useSuspenseInfiniteQuery)๋ฅผ ์ ๊ณตํ๋ [@suspensive/react-query](https://tanstack.com/query/v4/docs/react/community/suspensive-react-query)๋ฅผ ์๊ฐํ๊ณ ์๋ค.
- suspensive/react-query์ ํ (useSuspenseQuery, useSuspenseQueries, useSuspenseInfiniteQuery)์ @tanstack/react-query v5 ๋ฒ์ ์ ์ถ๊ฐ([๊ด๋ จ Pull Request](https://github.com/TanStack/query/pull/5739))๋๊ณ ๊ณต์ API๋ก [์ด ํ์ด์ง](https://tanstack.com/query/v5/docs/react/guides/suspense)์์ ํ์ธํ ์ ์์ต๋๋ค.
## Default Query Function
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- [Default Query Function ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/guides/default-query-function)
- ์ฑ ์ ์ฒด์์ ๋์ผํ ์ฟผ๋ฆฌ ํจ์๋ฅผ ๊ณต์ ํ๊ณ , `queryKey`๋ฅผ ์ฌ์ฉํด ๊ฐ์ ธ์์ผ ํ ๋ฐ์ดํฐ๋ฅผ ์๋ณํ๊ณ ์ถ๋ค๋ฉด `QueryClient`์ `queryFn` ์ต์ ์ ํตํด Default Query Function์ ์ง์ ํด ์ค ์ ์๋ค.```tsx
// ๊ธฐ๋ณธ ์ฟผ๋ฆฌ ํจ์
const getSuperHero = async ({ queryKey }: any) => {
const heroId = queryKey[1];return await axios.get(`http://localhost:4000/superheroes/${heroId}`);
};const queryClient = new QueryClient({
defaultOptions: {
queries: {
queryFn: getSuperHero,
// ...queries options
},
},
});function App() {
return (
{/* ... */}
);
}
```- `QueryClient`์ ์ฑ ์ ์ฒด์์ ์ฌ์ฉํ ์ฟผ๋ฆฌ ํจ์๋ฅผ ์ง์ ํด ์ค๋ค.
```tsx
// ์ฌ์ฉ ์์
const useSuperHeroData = (heroId: string) => {
return useQuery({ queryKey: ["super-hero", heroId] });
};
``````tsx
// ๋ค์ ํํ๋ ๋ถ๊ฐ๋ฅ
const useSuperHeroData = (heroId: string) => {
return useQuery({
queryKey: ["super-hero", heroId],
queryFn: () => getSuperHero(heroId),
});
};
```- useQuery์ ์ฒซ ๋ฒ์งธ ์ธ์๋ก `queryKey`๋ง ๋ฃ์ด์ฃผ๋ฉด ๋ ๋ฒ์งธ ์ธ์์ ๋ค์ด๊ฐ `queryFn`์ ์๋์ผ๋ก ์ค์ ๋ ๊ธฐ๋ณธ ์ฟผ๋ฆฌ ํจ์๊ฐ ๋ค์ด๊ฐ๋ค.
- ์ผ๋ฐ์ ์ผ๋ก `useQuery`๋ฅผ ์ฌ์ฉํ ๋์ ๋ฌ๋ฆฌ `queryFn`์ ์ง์ ํ์ง ์๊ธฐ์ ์ฟผ๋ฆฌ ํจ์์ ์ง์ ์ธ์๋ฅผ ๋ฃ์ด์ฃผ๋ ํํ์ ์ฌ์ฉ์ ๋ถ๊ฐ๋ฅํ๋ค.
## React Query Typescript
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- React Query๋ TypeScript์ `์ ๋ค๋ฆญ(Generics)`์ ๋ง์ด ์ฌ์ฉํ๋ค. ์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ค์ ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค์ง ์๊ณ API๊ฐ ๋ฐํํ๋ ๋ฐ์ดํฐ ์ ํ์ ์ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
### useQuery
ํ์ฌ useQuery๊ฐ ๊ฐ๊ณ ์๋ ์ ๋ค๋ฆญ์ `4๊ฐ`์ด๋ฉฐ, ๋ค์๊ณผ ๊ฐ๋ค.
1. **TQueryFnData**: useQuery๋ก ์คํํ๋ query function์ `์คํ ๊ฒฐ๊ณผ`์ ํ์ ์ ์ง์ ํ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค.
2. **TError**: query function์ `error` ํ์์ ์ ํ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค.
3. **TData**: useQuery์ `data์ ๋ด๊ธฐ๋ ์ค์ง์ ์ธ ๋ฐ์ดํฐ`์ ํ์ ์ ๋งํ๋ค. ์ฒซ ๋ฒ์งธ ์ ๋ค๋ฆญ๊ณผ์ ์ฐจ์ด์ ์ `select`์ ๊ฐ์ด query function์ ๋ฐํ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐ ํธ๋ค๋ง์ ํตํด ๋ฐํํ๋ ๊ฒฝ์ฐ์ ๋์ํ ์ ์๋ ํ์ ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ์ข๋ค.
4. **TQueryKey**: useQuery์ ์ฒซ ๋ฒ์งธ ์ธ์ `queryKey`์ ํ์ ์ ๋ช ์์ ์ผ๋ก ์ง์ ํด ์ฃผ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค.```tsx
// useQuery์ ํ์
export function useQuery<
TQueryFnData = unknown,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey
>
``````tsx
import { AxiosError } from "axios";// useQuery ํ์ ์ ์ฉ ์์
const { data } = useQuery<
AxiosResponse,
AxiosError,
string[],
["super-heroes", number]
>({
queryKey: ["super-heroes", id],
queryFn: getSuperHero,
select: (data) => {
const superHeroNames = data.data.map((hero) => hero.name);
return superHeroNames;
},
});/**
์ฃผ์ ํ์
* data: string[] | undefined
* error: AxiosError
* select: (data: AxiosResponse): string[]
*/
```
### useMutation
useMutation๋ useQuery์ ๋์ผํ๊ฒ ํ์ฌ 4๊ฐ์ด๋ฉฐ, ๋ค์๊ณผ ๊ฐ๋ค.
1. **TData**: useMutation์ ๋๊ฒจ์ค mutation function์ `์คํ ๊ฒฐ๊ณผ`์ ํ์ ์ ์ง์ ํ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค.
- data์ ํ์ ๊ณผ onSuccess(1๋ฒ์งธ ์ธ์) ์ธ์์ ํ์ ์ผ๋ก ํ์ฉ๋๋ค.
2. **TError**: useMutation์ ๋๊ฒจ์ค mutation function์ `error` ํ์์ ์ ํ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค.
3. **TVariables**: `mutate ํจ์`์ ์ ๋ฌ ํ ์ธ์๋ฅผ ์ง์ ํ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค.
- onSuccess(2๋ฒ์งธ ์ธ์), onError(2๋ฒ์งธ ์ธ์), onMutate(1๋ฒ์งธ ์ธ์), onSettled(3๋ฒ์งธ ์ธ์) ์ธ์์ ํ์ ์ผ๋ก ํ์ฉ๋๋ค.
4. **TContext**: mutation function์ ์คํํ๊ธฐ ์ ์ ์ํํ๋ `onMutate ํจ์์ return๊ฐ`์ ์ง์ ํ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค.
- onMutate์ ๊ฒฐ๊ณผ๊ฐ์ ํ์ ์ onSuccess(3๋ฒ์งธ ์ธ์), onError(3๋ฒ์งธ ์ธ์), onSettled(4๋ฒ์งธ ์ธ์)์์ ํ์ฉํ๋ ค๋ฉด ํด๋น ํ์ ์ ์ง์ ํด์ผ ํ๋ค.```tsx
export function useMutation<
TData = unknown,
TError = DefaultError,
TVariables = void,
TContext = unknown
>
``````tsx
// useMutation ํ์ ์ ์ฉ ์์
const { mutate } = useMutation({
mutationFn: postTodo,
onSuccess: (res, id, nextId) => {},
onError: (err, id, nextId) => {},
onMutate: (id) => id + 1,
onSettled: (res, err, id, nextId) => {},
});const onClick = () => {
mutate(5);
};/**
์ฃผ์ ํ์
* data: Todo
* error: AxiosError
* onSuccess: (res: Todo, id: number, nextId: number)
* onError: (err: AxiosError, id: number, nextId: number)
* onMutate: (id: number)
* onSettled: (res: Todo, err: AxiosError, id: number, nextId: number),
*/
```
### useInfiniteQuery
ํ์ฌ useInfiniteQuery ๊ฐ๊ณ ์๋ ์ ๋ค๋ฆญ์ `4๊ฐ`์ด๋ฉฐ, useQuery์ ์ ์ฌํ๋ค.
1. **TQueryFnData**: useInfiniteQuery๋ก ์คํํ๋ query function์ `์คํ ๊ฒฐ๊ณผ`์ ํ์ ์ ์ง์ ํ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค.
2. **TError**: query function์ `error` ํ์์ ์ ํ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค.
3. **TData**: useInfiniteQuery์ `data์ ๋ด๊ธฐ๋ ์ค์ง์ ์ธ ๋ฐ์ดํฐ`์ ํ์ ์ ๋งํ๋ค. ์ฒซ ๋ฒ์งธ ์ ๋ค๋ฆญ๊ณผ์ ์ฐจ์ด์ ์ `select`์ ๊ฐ์ด query function์ ๋ฐํ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐ ํธ๋ค๋ง์ ํตํด ๋ฐํํ๋ ๊ฒฝ์ฐ์ ๋์ํ ์ ์๋ ํ์ ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ์ข๋ค.
4. **TQueryKey**: useInfiniteQuery์ ์ฒซ ๋ฒ์งธ ์ธ์ `queryKey`์ ํ์ ์ ๋ช ์์ ์ผ๋ก ์ง์ ํด ์ฃผ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค.```tsx
export function useInfiniteQuery<
TQueryFnData = unknown,
TError = DefaultError,
TData = InfiniteData,
TQueryKey extends QueryKey = QueryKey
>
``````tsx
const {
data,
hasNextPage,
fetchNextPage,
//...
} = useInfiniteQuery<
AxiosResponse,
AxiosError,
InfiniteData, number>,
["colors"]
>({
queryKey: ["colors"],
queryFn: fetchColors,
initialPageParam: 1,
getNextPageParam: (lastPage, allPages) => {
return allPages.length < 4 && allPages.length + 1;
},
// ...options
});/**
์ฃผ์ ํ์
* data: InfiniteData, number> | undefined
* error: AxiosError
* select: (data: InfiniteData, number>): InfiniteData, number>
* getNextPageParam: GetNextPageParamFunction>
*/
```
### ๐ก Typescript Best Practice
- [TypeScript ๊ณต์ ๋ฌธ์](https://tanstack.com/query/v5/docs/react/typescript)
- ์์ ์ ๋ค๋ฆญ์ ๋ชจ๋ ์ฌ์ฉํ๋ ๊ฑด ์ฝ๋์ ๋ณต์ก๋๊ฐ ๋์ด๋๋ค. ํ์ง๋ง react query๋ ํ์ ์ ์ ์ ๋ฌํ๋ฏ๋ก ๊ตณ์ด ์ ๋ค๋ฆญ์ ๋ชจ๋ ์ง์ ์ ๊ณตํ ํ์๊ฐ ์๋ค.
- ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ `queryFn`์ ํ์ ์ ์ ์ ์ํด์ `ํ์ ์ถ๋ก `์ด ์ํํ๊ฒ ๋๊ฒ ํ๋ ๊ฒ์ด๋ค.```tsx
const fetchGroups = async (): Promise => {
return await axios.get("/groups");
};const { data } = useQuery({
queryKey: ["groups"],
queryFn: fetchGroups,
select: (data) => data.data,
});/**
์ฃผ์ ํ์
* data: AxiosResponse | undefined
* error: Error | null
* select: (data: AxiosResponse): Group[]
*/
```
## React Query ESLint Plugin
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- Tanstack Query๋ `์์ฒด ESLint Plugin`์ ์ ๊ณตํฉ๋๋ค. ํด๋น ํ๋ฌ๊ทธ์ธ์ ํตํด ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ์ ์ฉํ๊ณ , ์ผ๋ฐ์ ์ธ ์ค์๋ฅผ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
### ์ค์น
```bash
$ npm i -D @tanstack/eslint-plugin-query
# or
$ pnpm add -D @tanstack/eslint-plugin-query
# or
$ yarn add -D @tanstack/eslint-plugin-query
# or
$ bun add -D @tanstack/eslint-plugin-query
```
### ์ฌ์ฉ ๋ฐฉ๋ฒ(1)
- ํ๋ฌ๊ทธ์ธ์ ๋ํ `๊ถ์ฅํ๋ ๋ชจ๋ rule`์ ์ ์ฉํ๋ ค๋ฉด ์๋์ ๊ฐ์ด `.eslintrc.js` ํ์ผ์ `extends`๋ฐฐ์ด ์์ `plugin:@tanstack/eslint-plugin-query/recommended`์ ์ถ๊ฐํฉ๋๋ค.
```js
module.exports = {
// ...
extends: ["plugin:@tanstack/eslint-plugin-query/recommended"],
rules: {
/* ์์ ํ๊ณ ์ ํ๋ rule ์ถ๊ฐ ๊ฐ๋ฅ... */
},
};
```- ๋ฌผ๋ก , rule์ ๋ณ๊ฒฝํ๊ณ ์ถ๋ค๋ฉด rules์ ์๋ `์ฌ์ฉ๋ฐฉ๋ฒ(2)`์ ๊ฐ์ด rule์ ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค.
### ์ฌ์ฉ ๋ฐฉ๋ฒ(2)
- ์ํ๋ `rule`์ ๊ฐ๋ณ์ ์ผ๋ก ์ค์ ํด์ ์ ์ฉํ๋ ค๋ฉด ์๋์ ๊ฐ์ด `.eslintrc.js` ํ์ผ์ `plugins`๋ฐฐ์ด ์์ `@tanstack/query`๋ฅผ ์ถ๊ฐํ๊ณ , ์ ์ฉํ๊ณ ์ ํ๋ `rules`์ ๊ท์น์ ์ถ๊ฐํฉ๋๋ค.
```js
module.exports = {
// ...
plugins: ["@tanstack/query"],
rules: {
"@tanstack/query/exhaustive-deps": "error",
"@tanstack/query/no-rest-destructuring": "warn",
"@tanstack/query/stable-query-client": "error",
},
};
```
## ์ง์ ๋ฒ์
[๋ชฉ์ฐจ ์ด๋](#์ฃผ์-์ปจ์ -๋ฐ-๊ฐ์ด๋-๋ชฉ์ฐจ)
- Tanstack Query v5์ ํ์ํ TypeScript ์ต์ ๋ฒ์ ์ `v4.7`์ ๋๋ค.
- Tanstack Query v5์ ํ์ํ React ์ต์ ๋ฒ์ ์ `v18`์ ๋๋ค.- React v18 ์ด์์์ ์ง์ํ๋ `useSyncExternalStore` ํ ์ ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
- Tanstack Query v5์ ๋ธ๋ผ์ฐ์ ๋ณ ์ง์ ๋ฒ์ ์ ์๋์ ๊ฐ์ต๋๋ค.
```
Chrome >= 91
Firefox >= 90
Edge >= 91
Safari >= 15
iOS >= 15
opera >= 77
```