GithubHelp home page GithubHelp logo

donkeykong100 / react-query-tutorial Goto Github PK

View Code? Open in Web Editor NEW

This project forked from ssi02014/react-query-tutorial

1.0 0.0 0.0 413 KB

๐Ÿ˜ƒ react-query์—์„œ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ๊ฐœ๋… ์ •๋ฆฌ

TypeScript 93.28% HTML 6.72%

react-query-tutorial's Introduction

๐Ÿ’ป React Query (v4: TanStack Query)

  • ํ•ด๋‹น ์ €์žฅ์†Œ๋Š” react-query์—์„œ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋…๋“ค์„ ์ •๋ฆฌํ•œ ์ €์žฅ์†Œ์ž…๋‹ˆ๋‹ค. react-query์˜ ๋ชจ๋“  ํ™œ์šฉ ๋ฐฉ๋ฒ•์ด ์ž‘์„ฑ๋œ ์ƒํƒœ๋Š” ์•„๋‹ˆ๋ฉฐ, ํ•„์š”ํ•œ ๋‚ด์šฉ์€ ์ถ”๊ฐ€, ๋ณด์™„ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
  • ์˜คํƒˆ์ž ๋ฐ ๊ฐ€๋…์„ฑ์ด ์•ˆ ์ข‹๊ฑฐ๋‚˜ ์ˆ˜์ •์ด ํ•„์š”ํ•œ ๋‚ด์šฉ์€ Pull Request, Issue ๋“ฑ ์ž์œ ๋กญ๊ฒŒ ๋‚จ๊ฒจ์ฃผ์‹œ๋ฉด ๊ฒ€ํ†  ํ›„์— ๋ฐ˜์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋งŽ๊ด€๋ถ€ ๐Ÿ™‡โ€โ™‚๏ธ

๐ŸŒŸ Contributors

contributors


react-query v4 (TanStack Query) ์ •์‹ ๋ฆด๋ฆฌ์ฆˆ

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2022-08-17 แ„‹แ…ฉแ„’แ…ฎ 2 20 01

  • react-query v4๊ฐ€ ์ •์‹ ๋ฆด๋ฆฌ์ฆˆ๋˜๋ฉด์„œ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์•„๋ž˜ ๋ฌธ์„œ์— ์ถ”๊ฐ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • react-query v3 vs v4 ๋น„๊ต ๋ฌธ์„œ

์ฃผ์š” ์ปจ์…‰ ๋ฐ ๊ฐ€์ด๋“œ ๋ชฉ์ฐจ

  1. React-Query ๊ธฐ๋Šฅ
  2. ๊ธฐ๋ณธ ์„ค์ •(QueryClientProvider, QueryClient)
  3. React Query Devtools
  4. React Query ์บ์‹ฑ ๋ผ์ดํ”„ ์‚ฌ์ดํด
  5. useQuery
  6. useQuery ์ฃผ์š” ๋ฆฌํ„ด ๋ฐ์ดํ„ฐ
  7. staleTime๊ณผ cacheTime
  8. ๋งˆ์šดํŠธ ๋  ๋•Œ๋งˆ๋‹ค ์žฌ์š”์ฒญํ•˜๋Š” refetchOnMount
  9. ์œˆ๋„์šฐ๊ฐ€ ํฌ์ปค์‹ฑ ๋  ๋•Œ๋งˆ๋‹ค ์žฌ์š”์ฒญํ•˜๋Š” refetchOnWindowFocus
  10. Polling ๋ฐฉ์‹์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ refetchInterval์™€ refetchIntervalInBackground)
  11. ์ž๋™ ์‹คํ–‰์˜ enabled์™€ ์ˆ˜๋™์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋‹ค์‹œ ์š”์ฒญํ•˜๋Š” refetch
  12. ์‹คํŒจํ•œ ์ฟผ๋ฆฌ์— ๋Œ€ํ•ด ์žฌ์š”์ฒญํ•˜๋Š” retry
  13. onSuccess, onError, onSettled Callback
  14. select๋ฅผ ์ด์šฉํ•œ ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜
  15. Paginated ๊ตฌํ˜„์— ์œ ์šฉํ•œ keepPreviousData
  16. ์ฟผ๋ฆฌ๋ฅผ ๋ณ‘๋ ฌ(Parallel) ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋Š” useQueries
  17. ์ข…์† ์ฟผ๋ฆฌ(Dependent Queries)
  18. QueryClient ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” useQueryClient
  19. ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” initialData
  20. Infinite Queries(๋ฌดํ•œ ์ฟผ๋ฆฌ) + useInfiniteQuery
  21. ์„œ๋ฒ„์™€ HTTP CUD๊ด€๋ จ ์ž‘์—…์„ ์œ„ํ•œ useMutation๊ณผ mutate
  22. ์ฟผ๋ฆฌ๋ฅผ ๋ฌดํšจํ™”ํ•  ์ˆ˜ ์žˆ๋Š” queryClient.invalidateQueries
  23. ์บ์‹œ ๋ฐ์ดํ„ฐ ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธ๋ฅผ ์œ„ํ•œ queryClient.setQueryData
  24. ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX)์„ ์˜ฌ๋ ค์ฃผ๋Š” Optimistic Updates(๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ)
  25. ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ useQueryErrorResetBoundary
  26. ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ์— ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ ์šฉ

๐Ÿ“ƒ API Reference

  1. QueryClient ์ฃผ์š” ๋‚ด์šฉ ์ •๋ฆฌ ๋ฌธ์„œ(v3/v4)


๐Ÿ“ƒ React-Query ๊ฐœ์š” ๋ฐ ๊ธฐ๋Šฅ

๊ฐœ์š”

  • react-query๋Š” ๋ฆฌ์•กํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์„œ๋ฒ„ ์ƒํƒœ ๊ฐ€์ ธ์˜ค๊ธฐ, ์บ์‹ฑ, ๋™๊ธฐํ™” ๋ฐ ์—…๋ฐ์ดํŠธ๋ฅผ ๋ณด๋‹ค ์‰ฝ๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋ฉฐ ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ์™€ ์„œ๋ฒ„ ์ƒํƒœ๋ฅผ ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋งŒ๋“ค์–ด์ง„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.
  • react-query์—์„œ ๊ธฐ์กด ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(redux, mobX)๋Š” ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ ์ž‘์—…์— ์ ํ•ฉํ•˜์ง€๋งŒ ๋น„๋™๊ธฐ ๋˜๋Š” ์„œ๋ฒ„ ์ƒํƒœ ์ž‘์—…์—๋Š” ๊ทธ๋‹ค์ง€ ์ข‹์ง€ ์•Š๋‹ค๊ณ  ๋งํ•˜๊ณ  ์žˆ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ(Client State)์™€ ์„œ๋ฒ„ ์ƒํƒœ(Server State)๋Š” ์™„์ „ํžˆ ๋‹ค๋ฅด๋ฉฐ, ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฐ๊ฐ์˜ input ๊ฐ’์œผ๋กœ ์˜ˆ๋ฅผ ๋“ค ์ˆ˜ ์žˆ๊ณ  ์„œ๋ฒ„ ์ƒํƒœ๋Š” database์— ์ €์žฅ๋˜์–ด์žˆ๋Š” ๋ฐ์ดํ„ฐ๋กœ ์˜ˆ๋ฅผ ๋“ค ์ˆ˜ ์žˆ๋‹ค.

๊ธฐ๋Šฅ

  1. ์ž๋™

    • React Query์— ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์œ„์น˜์™€ ๋ฐ์ดํ„ฐ๊ฐ€ ์–ผ๋งˆ๋‚˜ ํ•„์š”ํ•œ์ง€ ์•Œ๋ ค์ฃผ๋ฉด ๋‚˜๋จธ์ง€๋Š” ์ž๋™์ด๋‹ค. React Query๋Š” ๊ตฌ์„ฑ์ด ํ•„์š” ์—†๋Š” ์ฆ‰์‹œ ์บ์‹ฑ, ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์—…๋ฐ์ดํŠธ ๋ฐ ์˜ค๋ž˜๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  2. ์นœ์ˆ™ํ•˜๊ณ  ๊ฐ„๋‹จํ•˜๋‹ค.

    • promise ๋˜๋Š” async/await๋กœ ์ž‘์—…ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ๋‹ค๋ฉด ์ด๋ฏธ React Query๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ๋Š” ๊ฒƒ์ด๋‹ค. ๊ด€๋ฆฌํ•  ์ „์—ญ ์ƒํƒœ, ๊ฐ์†๊ธฐ, ์ •๊ทœํ™” ์‹œ์Šคํ…œ ๋˜๋Š” ์ดํ•ดํ•ด์•ผ ํ•  ๋ฌด๊ฑฐ์šด ๊ตฌ์„ฑ์ด ์—†๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”(๋˜๋Š” ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š”) ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.
  3. ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ ๋ฐ ๊ตฌ์„ฑ

    • React Query๋Š” ๋ชจ๋“  ์‚ฌ์šฉ ์‚ฌ๋ก€์— ๋งž๋Š” ๋…ธ๋ธŒ์™€ ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ์˜ ๊ฐ ๊ด€์ฐฐ์ž ์ธ์Šคํ„ด์Šค๊นŒ์ง€ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์ „์šฉ devtools, ๋ฌดํ•œ ๋กœ๋”ฉ API ๋ฐ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ๋ฅผ ์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ๋Š” mutation ๋„๊ตฌ๊ฐ€ ์žˆ๋‹ค.
  4. ์ ์€ ์ฝ”๋“œ. ๋” ์ ์€ ์—ฃ์ง€ ์ผ€์ด์Šค.

    • ๋ฆฌ๋“€์„œ, ์บ์‹ฑ ๋กœ์ง, ํƒ€์ด๋จธ, ์žฌ์‚ฌ์šฉ ๋กœ์ง, ๋ณต์žกํ•œ ๋น„๋™๊ธฐ/๋Œ€๊ธฐ ์Šคํฌ๋ฆฝํŒ…์„ ํ‰์†Œ ํ•˜๋˜ ์ฝ”๋“œ๋ณด๋‹ค ์ ์€ ์–‘์˜ ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

React-Query ๊ธฐ๋ณธ ์„ค์ •

๋ชฉ์ฐจ ์ด๋™

// QueryClient ์˜ˆ์ œ
import { QueryClient } from "react-query";

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: Infinity,
      // ...
    },
  },
});
  • QueryClient๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์บ์‹œ์™€ ์ƒํ˜ธ ์ž‘์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • QueryClient์—์„œ ๋ชจ๋“  query ๋˜๋Š” mutation์— ๊ธฐ๋ณธ ์˜ต์…˜๋“ค์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ข…๋ฅ˜๊ฐ€ ์ƒ๋‹นํ•˜๋‹ˆ ์ƒ๋‹จ์— ๊ณต์‹ ์‚ฌ์ดํŠธ๋ฅผ ์ฐธ๊ณ ํ•˜์ž.
// QueryClientProvider + QueryClient
import { QueryClient, QueryClientProvider } from "react-query";

const queryClient = new QueryClient();

function App() {
  return (
   <QueryClientProvider client={queryClient}>
      <div>๋ธ”๋ผ๋ธ”๋ผ</div>
   </QueryClientProvider>;
  );
}
  • App ์ „์ฒด์—์„œ ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” QueryClientProvider๋ฅผ ์ตœ์ƒ๋‹จ์—์„œ ๊ฐ์‹ธ์ฃผ๊ณ  QueryClient๋ฅผ Props๋กœ ๋„ฃ์–ด์ค˜์•ผ ํ•œ๋‹ค.
  • App.js์— QueryClientProvider๋กœ ์ดํ•˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ๊ณ  QueryClient๋ฅผ ๋‚ด๋ ค๋ณด๋‚ด ์คŒ โ‡’ ์ด context๋Š” ์•ฑ์—์„œ ๋น„๋™๊ธฐ ์š”์ฒญ์„ ์•Œ์•„์„œ ์ฒ˜๋ฆฌํ•˜๋Š” background ๊ณ„์ธต์ด ๋œ๋‹ค.
  • QueryClientProvider ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ App์— QueryClient๋ฅผ ์—ฐ๊ฒฐํ•˜๊ณ  ์ œ๊ณตํ•œ๋‹ค.

Devtools

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2022-04-07 แ„‹แ…ฉแ„’แ…ฎ 11 53 32

๋ชฉ์ฐจ ์ด๋™

  • React Query๋Š” ์ „์šฉ devtools๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • devtools๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด React Query์˜ ๋ชจ๋“  ๋‚ด๋ถ€ ๋™์ž‘์„ ์‹œ๊ฐํ™”ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๋ฉฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋””๋ฒ„๊น… ์‹œ๊ฐ„์„ ์ ˆ์•ฝํ•  ์ˆ˜ ์žˆ๋‹ค.
import { ReactQueryDevtools } from "react-query/devtools";

<AppContext.Provider value={user}>
  <QueryClientProvider client={queryClient}>
    // ...
    <ReactQueryDevtools initialIsOpen={false} position="bottom-right" />
  </QueryClientProvider>
</AppContext.Provider>;
  • initialIsOpen (Boolean)

    • true์ด๋ฉด ๊ฐœ๋ฐœ ๋„๊ตฌ๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์—ด๋ ค ์žˆ๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • position?: ("top-left" | "top-right" | "bottom-left" | "bottom-right")

    • ๊ธฐ๋ณธ๊ฐ’: bottom-left
    • devtools ํŒจ๋„์„ ์—ด๊ณ  ๋‹ซ๊ธฐ ์œ„ํ•œ ๋กœ๊ณ  ์œ„์น˜
  • ์ผ๋ฐ˜์ ์œผ๋กœ initialIsOpen, position์„ ์ž์ฃผ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ์•„๋ž˜์™€ ๊ฐ™์€ ์˜ต์…˜๋“ค๋„ ์กด์žฌํ•œ๋‹ค.

  • panelProps

    • ํŒจ๋„์— props์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด className, style, onClick ๋“ฑ
  • closeButtonProps

    • ๋‹ซ๊ธฐ ๋ฒ„ํŠผ์— props๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • toggleButtonProps

    • ํ† ๊ธ€ ๋ฒ„ํŠผ์— props๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Devtools๋Š” ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ process.env.NODE_ENV === 'development' ์ธ ๊ฒฝ์šฐ์—๋งŒ ์‹คํ–‰๋œ๋‹ค, ์ฆ‰ ์ผ๋ฐ˜์ ์œผ๋กœ ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ๋งŒ ์ž‘๋™ํ•˜๋ฏ€๋กœ ์„ค์ •๋˜์–ด์žˆ์œผ๋ฏ€๋กœ ํ”„๋กœ์ ํŠธ ๋ฐฐํฌ ์‹œ์— Devtools ์‚ฝ์ž…์ฝ”๋“œ๋ฅผ ์ œ๊ฑฐํ•ด์ค„ ํ•„์š”๊ฐ€ ์—†๋‹ค.


์บ์‹ฑ ๋ผ์ดํ”„ ์‚ฌ์ดํด

๋ชฉ์ฐจ ์ด๋™

  • React-Query ์บ์‹œ ๋ผ์ดํ”„ ์‚ฌ์ดํด
* Query Instances with and without cache data(์บ์‹œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๊ฑฐ๋‚˜ ์—†๋Š” ์ฟผ๋ฆฌ ์ธ์Šคํ„ด์Šค)
* Background Refetching(๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋ฆฌํŒจ์นญ)
* Inactive Queries(๋น„ํ™œ์„ฑ ์ฟผ๋ฆฌ)
* Garbage Collection(๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜)
  • cacheTime์˜ ๊ธฐ๋ณธ๊ฐ’ 5๋ถ„, staleTime ๊ธฐ๋ณธ๊ฐ’ 0๋ถ„์„ ๊ฐ€์ •
  1. A๋ผ๋Š” queryKey๋ฅผ ๊ฐ€์ง„ A ์ฟผ๋ฆฌ ์ธ์Šคํ„ด์Šค๊ฐ€ mount๋จ
  2. ๋„คํŠธ์›Œํฌ์—์„œ ๋ฐ์ดํ„ฐ fetchํ•˜๊ณ , ๋ถˆ๋Ÿฌ์˜จ ๋ฐ์ดํ„ฐ๋Š” A๋ผ๋Š” queryKey๋กœ ์บ์‹ฑํ•จ
  3. ์ด ๋ฐ์ดํ„ฐ๋Š” fresh์ƒํƒœ์—์„œ staleTime(๊ธฐ๋ณธ๊ฐ’ 0) ์ดํ›„ stale ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ๋จ
  4. A ์ฟผ๋ฆฌ ์ธ์Šคํ„ด์Šค๊ฐ€ unmount๋จ
  5. ์บ์‹œ๋Š” cacheTime(๊ธฐ๋ณธ๊ฐ’ 5min) ๋งŒํผ ์œ ์ง€๋˜๋‹ค๊ฐ€ ๊ฐ€๋น„์ง€ ์ฝœ๋ ‰ํ„ฐ(GC)๋กœ ์ˆ˜์ง‘๋จ
  6. ๋งŒ์ผ, cacheTime์ด ์ง€๋‚˜๊ธฐ ์ „์ด๊ณ , A ์ฟผ๋ฆฌ ์ธ์Šคํ„ด์Šค freshํ•œ ์ƒํƒœ๋ผ๋ฉด ์ƒˆ๋กญ๊ฒŒ mount๋˜๋ฉด ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.

useQuery

useQuery ๊ธฐ๋ณธ ๋ฌธ๋ฒ•

๋ชฉ์ฐจ ์ด๋™

// ์‚ฌ์šฉ๋ฒ•(1)
const { data, isLoading, ... } =  useQuery(queryKey, queryFn, {
  //์˜ต์…˜๋“ค ex) enabled, staleTime
});

// ์‚ฌ์šฉ๋ฒ•(2)
const result = useQuery({
  queryKey,
  queryFn,
  //์˜ต์…˜๋“ค ex) enabled, staleTime
});
// ์‹ค์ œ ์˜ˆ์ œ
const getSuperHero = useCallback(() => {
  return axios.get("http://localhost:4000/superheroes");
}, []);

const { data, isLoading } = useQuery(["super-heroes"], getSuperHero);
  • useQuery๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ 3๊ฐœ์˜ ์ธ์ž๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ์ธ์ž๊ฐ€ queryKey(ํ•„์ˆ˜), ๋‘ ๋ฒˆ์งธ ์ธ์ž๊ฐ€ queryFn(ํ•„์ˆ˜), ์„ธ ๋ฒˆ์งธ ์ธ์ž๊ฐ€ options์ž…๋‹ˆ๋‹ค.
  • useQuery๋Š” ์ฒซ ๋ฒˆ์งธ ์ธ์ž์ธ queryKey๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฐ์ดํ„ฐ ์บ์‹ฑ์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ž์—ด ๋˜๋Š” ๋ฐฐ์—ด๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ์œ„ ์˜ˆ์ œ์ฒ˜๋Ÿผ ๋ฌธ์ž์—ด๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋งŒ์•ฝ ์ฟผ๋ฆฌ๊ฐ€ ํŠน์ • ๋ณ€์ˆ˜์— ์˜์กดํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์•„๋ž˜ ์˜ˆ์ œ์ฒ˜๋Ÿผ ๋ฐฐ์—ด๋กœ ์ง€์ •ํ•ด ํ•ด๋‹น ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    • ์ฐธ๊ณ ๋กœ ํ•ด๋‹น ๋‚ด์šฉ์€ v3๊ธฐ์ค€์ด๊ณ , react-query v4๋ถ€ํ„ฐ๋Š” ๋ฌด์กฐ๊ฑด ๋ฐฐ์—ด๋กœ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ๋ฒ• (1)๊ณผ (2)๋ฒˆ ๋‘˜ ๋‹ค ์‚ฌ์šฉ๋˜๋Š”๋ฐ. ์ ‘๊ทผ ๋ฐฉ์‹์˜ ์ฐจ์ด์ž…๋‹ˆ๋‹ค. ๋‘ ๊ฐ€์ง€ ๋ฐฉ์‹ ๋ชจ๋‘ ์ž˜ ์ดํ•ดํ•˜๊ณ  ์‚ฌ์šฉํ•ฉ์‹œ๋‹ค.

// (1)
const fetchSuperHero = ({ queryKey }: any) => {
  const heroId = queryKey[1]; // queryKey: (2) ['super-hero', '3']
  return axios.get(`http://localhost:4000/superheroes/${heroId}`);
};
const useSuperHeroData = (heroId: string) => {
  return useQuery(["super-hero", heroId], fetchSuperHero);
};
// (2)
const fetchSuperHero = (heroId: string) => {
  return axios.get(`http://localhost:4000/superheroes/${heroId}`);
};
const useSuperHeroData = (heroId: string) => {
  return useQuery(["super-hero", heroId], () => fetchSuperHero(heroId));
};
  • useQuery์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ž์ธ queryFn๋Š” Promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋„ฃ์–ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • useQuery์˜ ์„ธ ๋ฒˆ์งธ ์ธ์ž์ธ options์— ๋งŽ์ด ์“ฐ์ด๋Š” ์˜ต์…˜๋“ค์„ ๋ฐ‘์—์„œ ์„ค๋ช…ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ ์™ธ์—๋Š” ์œ„์— useQuery ์ฐธ๊ณ  ์‚ฌ์ดํŠธ๋ฅผ ํ†ตํ•ด ํ™•์ธํ•˜๋ฉด ๋œ๋‹ค.

  • ์ฐธ๊ณ ๋กœ ๋‚˜์ค‘์— queryClient๋กœ ํŠน์ • key์— ํ•ด๋‹นํ•˜๋Š” query์— ์ ‘๊ทผํ•  ๋•Œ๋Š” ์ดˆ๊ธฐ์— ์„ค์ •ํ•ด๋‘” ํฌ๋งท์„ ์ง€์ผœ์ค˜์•ผ ์ œ๋Œ€๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
  • ์•„๋ž˜ ์˜ˆ์ œ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด useQuery์—์„œ queryKey์— ํ•ด๋‹นํ•˜๋Š” ํฌ๋งท์ด ๋ฐฐ์—ด["super-hero", heroId]์ด๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ๋ฐ‘์— useMutation์—์„œ setQueryData๋ฅผ ์ด์šฉํ•  ๋•Œ ๋˜‘๊ฐ™์ด ["super-hero", heroId] ํฌ๋งท์„ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค.

// ์˜ˆ
const useSuperHeroData = (heroId: string) => {
  return useQuery(["super-hero", heroId], () => fetchSuperHero(heroId));
};

const useAddSuperHeroData = (heroId: string) => {
  const queryClient = useQueryClient();

  return useMutation(addSuperHero, {
    onSuccess(data) {
      // ํฌ๋งท์ด ์•ˆ ๋งž์•„์„œ ํ•ด๋‹น ์ฟผ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๋Œ€๋กœ ๋ชป ๊ฐ€์ ธ์˜ด!
      // ["super-hero", heroId]๋กœ ํ•ด์•ผ ๋จ
      queryClient.setQueryData(["super-hero"], (oldData: any) => {
        return {
          ...oldData,
          data: [...oldData.data, data.data],
        };
      });
    },
    onError(err) {
      console.log(err);
    },
  });
};

useQuery ์ฃผ์š” ๋ฆฌํ„ด ๋ฐ์ดํ„ฐ

๋ชฉ์ฐจ ์ด๋™

const { status, isLoading, isError, error, data, isFetching, ... } = useQuery(
  ["colors", pageNum],
  () => fetchColors(pageNum)
);
  • status: ์ฟผ๋ฆฌ ์š”์ฒญ ํ•จ์ˆ˜์˜ ์ƒํƒœ๋ฅผ ํ‘œํ˜„ํ•˜๋Š” status๋Š” 4๊ฐ€์ง€์˜ ๊ฐ’์ด ์กด์žฌํ•œ๋‹ค.(๋ฌธ์ž์—ด ํ˜•ํƒœ)
    • idle: ์ฟผ๋ฆฌ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๊ณ  ๋น„์—ˆ์„ ๋•Œ, { enabled: false } ์ƒํƒœ๋กœ ์ฟผ๋ฆฌ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ์ด ์ƒํƒœ๋กœ ์‹œ์ž‘๋œ๋‹ค.
      • ์ฐธ๊ณ ๋กœ v4๋ถ€ํ„ฐ๋Š” idle ์ƒํƒœ๋Š” ์ œ๊ฑฐ๋œ๋‹ค.
    • loading: ๋ง ๊ทธ๋Œ€๋กœ ์•„์ง ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๊ณ  ๋กœ๋”ฉ์ค‘์ผ ๋•Œ ์ƒํƒœ
    • error: ์š”์ฒญ ์—๋Ÿฌ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ƒํƒœ
    • success: ์š”์ฒญ ์„ฑ๊ณตํ–ˆ์„ ๋•Œ ์ƒํƒœ
  • data: ์ฟผ๋ฆฌ ํ•จ์ˆ˜๊ฐ€ ๋ฆฌํ„ดํ•œ Promise์—์„œ resolved๋œ ๋ฐ์ดํ„ฐ
  • isLoading: ์บ์‹ฑ ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์„ ๋•Œ! ์ฆ‰, ์ฒ˜์Œ ์‹คํ–‰๋œ ์ฟผ๋ฆฌ ์ผ ๋•Œ ๋กœ๋”ฉ ์—ฌ๋ถ€์— ๋”ฐ๋ผ true/false๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค.
    • ์ด๋Š” ์บ์‹ฑ ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋‹ค๋ฉด ๋กœ๋”ฉ ์—ฌ๋ถ€์— ์ƒ๊ด€์—†์ด false๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • isFetching: ์บ์‹ฑ ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋”๋ผ๋„ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜๋ฉด ๋กœ๋”ฉ ์—ฌ๋ถ€์— ๋”ฐ๋ผ true/false๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค.
    • ์ด๋Š” ์บ์‹ฑ ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋”๋ผ๋„ ์ฟผ๋ฆฌ ๋กœ๋”ฉ ์—ฌ๋ถ€์— ๋”ฐ๋ผ true/false๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • error: ์ฟผ๋ฆฌ ํ•จ์ˆ˜์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ, ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ์˜ค๋ฅ˜ ๊ฐ์ฒด
  • isError: ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ true
  • ๊ทธ ์™ธ ๋ฆฌํ„ด ๋ฐ์ดํ„ฐ๋“ค์„ ์ž์„ธํžˆ ์•Œ๊ณ  ์‹ถ์œผ๋ฉด ๊ณต์‹ ์‚ฌ์ดํŠธ ์ฐธ๊ณ 

useQuery ์ฃผ์š” ์˜ต์…˜

๋ชฉ์ฐจ ์ด๋™

staleTime cacheTime

  • stale์€ ์šฉ์–ด ๋œป๋Œ€๋กœ ์ฉ์€ ์ด๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค. ์ฆ‰, ์ตœ์‹  ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค.
  • fresh๋Š” ๋œป ๊ทธ๋Œ€๋กœ ์‹ ์„ ํ•œ ์ด๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค. ์ฆ‰, ์ตœ์‹  ์ƒํƒœ๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค.
const { isLoading, isFetching, data, isError, error } = useQuery(
  ["super-heroes"],
  getSuperHero,
  {
    cacheTime: 3000,
    staleTime: 50000,
  }
);

  1. staleTime (number | Infinity)
    • staleTime์€ ๋ฐ์ดํ„ฐ๊ฐ€ fresh์—์„œ stale ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ๋˜๋Š” ๋ฐ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„, ๋งŒ์•ฝ staleTime์ด 3000์ด๋ฉด fresh์ƒํƒœ์—์„œ 3์ดˆ ๋’ค์— stale๋กœ ๋ณ€ํ™˜
    • fresh ์ƒํƒœ์ผ ๋•Œ๋Š” ์ฟผ๋ฆฌ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒˆ๋กญ๊ฒŒ mount ๋˜์–ด๋„ ๋„คํŠธ์›Œํฌ ์š”์ฒญ(fetch)์ด ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค.
    • ๋ฐ์ดํ„ฐ๊ฐ€ ํ•œ๋ฒˆ fetch ๋˜๊ณ  ๋‚˜์„œ staleTime์ด ์ง€๋‚˜์ง€ ์•Š์•˜๋‹ค๋ฉด(fresh์ƒํƒœ) unmount ํ›„ ๋‹ค์‹œ mount ๋˜์–ด๋„ fetch๊ฐ€ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค.
    • staleTime์˜ ๊ธฐ๋ณธ๊ฐ’์€ 0์ด๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋ฐ˜์ ์œผ๋กœ fetch ํ›„์— ๋ฐ”๋กœ stale์ด ๋œ๋‹ค.
  2. cacheTime (number | Infinity)
    • ๋ฐ์ดํ„ฐ๊ฐ€ inactive ์ƒํƒœ์ผ ๋•Œ ์บ์‹ฑ ๋œ ์ƒํƒœ๋กœ ๋‚จ์•„์žˆ๋Š” ์‹œ๊ฐ„
    • ์ฟผ๋ฆฌ ์ธ์Šคํ„ด์Šค๊ฐ€ unmount ๋˜๋ฉด ๋ฐ์ดํ„ฐ๋Š” inactive ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ๋˜๋ฉฐ, ์บ์‹œ๋Š” cacheTime๋งŒํผ ์œ ์ง€๋œ๋‹ค.
    • cacheTime์ด ์ง€๋‚˜๋ฉด ๊ฐ€๋น„์ง€ ์ฝœ๋ ‰ํ„ฐ๋กœ ์ˆ˜์ง‘๋œ๋‹ค.
    • cacheTime์ด ์ง€๋‚˜๊ธฐ ์ „์— ์ฟผ๋ฆฌ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋‹ค์‹œ mount ๋˜๋ฉด, ๋ฐ์ดํ„ฐ๋ฅผ fetchํ•˜๋Š” ๋™์•ˆ ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.
    • cacheTime์€ staleTime๊ณผ ๊ด€๊ณ„์—†์ด, ๋ฌด์กฐ๊ฑด inactive ๋œ ์‹œ์ ์„ ๊ธฐ์ค€์œผ๋กœ ์บ์‹œ ๋ฐ์ดํ„ฐ ์‚ญ์ œ๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค.
    • cacheTime์˜ ๊ธฐ๋ณธ๊ฐ’์€ 5๋ถ„์ด๋‹ค.
  • ์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•  ์ ์€ staleTime๊ณผ cacheTime์˜ ๊ธฐ๋ณธ๊ฐ’์€ ๊ฐ๊ฐ 0๋ถ„๊ณผ 5๋ถ„์ด๋‹ค. ๋”ฐ๋ผ์„œ staleTime์— ์–ด๋– ํ•œ ์„ค์ •๋„ ํ•˜์ง€ ์•Š์œผ๋ฉด ์บ์‹ฑ์ด ์ „ํ˜€ ๋˜์ง€ ์•Š๋Š”๋‹ค. ์™œ๋ƒํ•˜๋ฉด, ํ•ญ์ƒ ์บ์‹ฑ ๋˜์–ด ์žˆ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ staleํ•˜๋‹ค๊ณ  ์—ฌ๊ธฐ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
  • staleTime์„ ๊ธธ๊ฒŒ ์„ค์ •ํ•˜๋”๋ผ๋„ cacheTime์ด ์งง๋‹ค๋ฉด ์ด ๋˜ํ•œ ์บ์‹ฑ์ด ์›ํ™œํ•˜๊ฒŒ ์ง„ํ–‰๋˜์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค. ๊ฒฐ๊ตญ์—๋Š” ๋‘ ๊ฐœ์˜ ์˜ต์…˜์„ ์ ์ ˆํ•˜๊ฒŒ ์„ค์ •ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

refetchOnMount

๋ชฉ์ฐจ ์ด๋™

const { isLoading, isFetching, data, isError, error } = useQuery(
  ["super-heroes"],
  getSuperHero,
  {
    refetchOnMount: true,
  }
);
  • refetchOnMount (boolean | "always")
  • refetchOnMount๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ stale ์ƒํƒœ์ผ ๊ฒฝ์šฐ, mount๋งˆ๋‹ค refetch๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์˜ต์…˜์ด๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ true์ด๋‹ค.
  • always ๋กœ ์„ค์ •ํ•˜๋ฉด ๋งˆ์šดํŠธ ์‹œ๋งˆ๋‹ค ๋งค๋ฒˆ refetch๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.
  • false๋กœ ์„ค์ •ํ•˜๋ฉด ์ตœ์ดˆ fetch ์ดํ›„์—๋Š” refetchํ•˜์ง€ ์•Š๋Š”๋‹ค.

refetchOnWindowFocus

๋ชฉ์ฐจ ์ด๋™

const { isLoading, isFetching, data, isError, error } = useQuery(
  "super-heroes",
  getSuperHero,
  {
    refetchOnWindowFocus: true,
  }
);
  • refetchOnWindowFocus (boolean | "always")
  • refetchOnWindowFocus๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ stale ์ƒํƒœ์ผ ๊ฒฝ์šฐ ์œˆ๋„์šฐ ํฌ์ปค์‹ฑ ๋  ๋•Œ๋งˆ๋‹ค refetch๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์˜ต์…˜์ด๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ true์ด๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค์–ด, ํฌ๋กฌ์—์„œ ๋‹ค๋ฅธ ํƒญ์„ ๋ˆŒ๋ €๋‹ค๊ฐ€ ๋‹ค์‹œ ์›๋ž˜ ๋ณด๋˜ ์ค‘์ธ ํƒญ์„ ๋ˆŒ๋ €์„ ๋•Œ๋„ ์ด ๊ฒฝ์šฐ์— ํ•ด๋‹นํ•œ๋‹ค. ์‹ฌ์ง€์–ด F12๋กœ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ ์ฐฝ์„ ์ผœ์„œ ๋„คํŠธ์›Œํฌ ํƒญ์ด๋“ , ์ฝ˜์†” ํƒญ์ด๋“  ๊ฐœ๋ฐœ์ž ๋„๊ตฌ ์ฐฝ์—์„œ ๋†€๋‹ค๊ฐ€ ํŽ˜์ด์ง€ ๋‚ด๋ถ€๋ฅผ ๋‹ค์‹œ ํด๋ฆญํ–ˆ์„ ๋•Œ๋„ ์ด ๊ฒฝ์šฐ์— ํ•ด๋‹นํ•œ๋‹ค.
  • always ๋กœ ์„ค์ •ํ•˜๋ฉด ํ•ญ์ƒ ์œˆ๋„์šฐ ํฌ์ปค์‹ฑ ๋  ๋•Œ๋งˆ๋‹ค refetch๋ฅผ ์‹คํ–‰ํ•œ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.

Polling

๋ชฉ์ฐจ ์ด๋™

const { isLoading, isFetching, data, isError, error } = useQuery(
  ["super-heroes"],
  getSuperHero,
  {
    refetchInterval: 2000,
    refetchIntervalInBackground: true,
  }
);
  • Polling(ํด๋ง)์ด๋ž€? ๋ฆฌ์–ผํƒ€์ž„ ์›น์„ ์œ„ํ•œ ๊ธฐ๋ฒ•์œผ๋กœ ์ผ์ •ํ•œ ์ฃผ๊ธฐ(ํŠน์ •ํ•œ ์‹œ๊ฐ„)๋ฅผ ๊ฐ€์ง€๊ณ  ์„œ๋ฒ„์™€ ์‘๋‹ต์„ ์ฃผ๊ณ ๋ฐ›๋Š” ๋ฐฉ์‹์ด ํด๋ง ๋ฐฉ์‹์ด๋‹ค.
  • react-query์—์„œ๋Š” refetchInterval, refetchIntervalInBackground์„ ์ด์šฉํ•ด์„œ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • refetchInterval์€ ์‹œ๊ฐ„(ms)๋ฅผ ๊ฐ’์œผ๋กœ ๋„ฃ์–ด์ฃผ๋ฉด ์ผ์ • ์‹œ๊ฐ„๋งˆ๋‹ค ์ž๋™์œผ๋กœ refetch๋ฅผ ์‹œ์ผœ์ค€๋‹ค.
  • refetchIntervalInBackground๋Š” refetchInterval๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ์˜ต์…˜์ด๋‹ค. ํƒญ/์ฐฝ์ด ๋ฐฑ๊ทธ๋ผ์šด๋“œ์— ์žˆ๋Š” ๋™์•ˆ refetch ์‹œ์ผœ์ค€๋‹ค. ์ฆ‰, ๋ธŒ๋ผ์šฐ์ €์— focus๋˜์–ด ์žˆ์ง€ ์•Š์•„๋„ refetch๋ฅผ ์‹œ์ผœ์ฃผ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

enabled refetch

๋ชฉ์ฐจ ์ด๋™

const { isLoading, isFetching, data, isError, error, refetch } = useQuery(
  ["super-heroes"],
  getSuperHero,
  {
    enabled: false,
  }
);

const handleClickRefetch = useCallback(() => {
  refetch();
}, [refetch]);

return (
  <div>
    {data?.data.map((hero: Data) => (
      <div key={hero.id}>{hero.name}</div>
    ))}
    <button onClick={handleClickRefetch}>Fetch Heroes</button>
  </div>
);
  • enabled๋Š” ์ฟผ๋ฆฌ๊ฐ€ ์ž๋™์œผ๋กœ ์‹คํ–‰๋˜์ง€ ์•Š๋„๋ก ํ•  ๋•Œ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. false๋ฅผ ์ฃผ๋ฉด ์ž๋™ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. ๋˜ํ•œ, useQuery ๋ฆฌํ„ด ๋ฐ์ดํ„ฐ ์ค‘ status๊ฐ€ idle ์ƒํƒœ๋กœ ์‹œ์ž‘ํ•œ๋‹ค.
  • refetch๋Š” ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜๋™์œผ๋กœ ๋‹ค์‹œ ์š”์ฒญํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค. ์ฟผ๋ฆฌ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์˜ค๋ฅ˜๋งŒ ๊ธฐ๋ก๋œ๋‹ค. ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ ค๋ฉด throwOnError์†์„ฑ์„ true๋กœ ํ•ด์„œ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค.
  • ๋ณดํ†ต ์ž๋™์œผ๋กœ ์ฟผ๋ฆฌ ์š”์ฒญ์„ ํ•˜์ง€ ์•Š๊ณ  ๋ฒ„ํŠผ ํด๋ฆญ์ด๋‚˜ ํŠน์ • ์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด ์š”์ฒญ์„ ์‹œ๋„ํ•  ๋•Œ ๊ฐ™์ด ์‚ฌ์šฉํ•œ๋‹ค.
  • ๋งŒ์•ฝ enabled: false๋ฅผ ์คฌ๋‹ค๋ฉด queryClient๊ฐ€ ์ฟผ๋ฆฌ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ• ์ค‘ invalidateQueries์™€ refetchQueries๋ฅผ ๋ฌด์‹œํ•œ๋‹ค.

retry

๋ชฉ์ฐจ ์ด๋™

const result = useQuery(["todos", 1], fetchTodoListPage, {
  retry: 10, // ์˜ค๋ฅ˜๋ฅผ ํ‘œ์‹œํ•˜๊ธฐ ์ „์— ์‹คํŒจํ•œ ์š”์ฒญ์„ 10๋ฒˆ ์žฌ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.
});
  • retry (boolean | number | (failureCount: number, error: TError) => boolean)
  • retry๋Š” ์ฟผ๋ฆฌ๊ฐ€ ์‹คํŒจํ•˜๋ฉด useQuery๋ฅผ ํŠน์ • ํšŸ์ˆ˜(๊ธฐ๋ณธ๊ฐ’ 3)๋งŒํผ ์žฌ์š”์ฒญํ•˜๋Š” ์˜ต์…˜์ด๋‹ค.
  • retry๊ฐ€ false์ธ ๊ฒฝ์šฐ, ์‹คํŒจํ•œ ์ฟผ๋ฆฌ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹ค์‹œ ์‹œ๋„ํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • true์ธ ๊ฒฝ์šฐ์—๋Š” ์‹คํŒจํ•œ ์ฟผ๋ฆฌ์— ๋Œ€ํ•ด์„œ ๋ฌดํ•œ ์žฌ์š”์ฒญ์„ ์‹œ๋„ํ•œ๋‹ค.
  • ๊ฐ’์œผ๋กœ ์ˆซ์ž๋ฅผ ๋„ฃ์„ ๊ฒฝ์šฐ, ์‹คํŒจํ•œ ์ฟผ๋ฆฌ๊ฐ€ ํ•ด๋‹น ์ˆซ์ž๋ฅผ ์ถฉ์กฑํ•  ๋•Œ๊นŒ์ง€ ์š”์ฒญ์„ ์žฌ์‹œ๋„ํ•œ๋‹ค.

onSuccess onError onSettled

๋ชฉ์ฐจ ์ด๋™

const onSuccess = useCallback((data) => {
  console.log("Success", data);
}, []);

const onError = useCallback((err) => {
  console.log("Error", err);
}, []);

const onSettled = useCallback(() => {
  console.log("Settled");
}, []);

const { isLoading, isFetching, data, isError, error, refetch } = useQuery(
  ["super-heroes"],
  getSuperHero,
  {
    onSuccess,
    onError,
    onSettled,
  }
);
  • onSuccess ํ•จ์ˆ˜๋Š” ์ฟผ๋ฆฌ ์š”์ฒญ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ง„ํ–‰๋ผ์„œ ์ƒˆ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ ์บ์‹œ๊ฐ€ ์—…๋ฐ์ดํŠธ๋  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋œ๋‹ค.
  • onError ํ•จ์ˆ˜๋Š” ์ฟผ๋ฆฌ์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์˜ค๋ฅ˜๊ฐ€ ์ „๋‹ฌ๋˜๋ฉด ์‹คํ–‰๋œ๋‹ค.
  • onSettled ํ•จ์ˆ˜๋Š” ์ฟผ๋ฆฌ ์š”์ฒญ์ด ์„ฑ๊ณต, ์‹คํŒจ ๋ชจ๋‘ ์‹คํ–‰๋œ๋‹ค.

select

๋ชฉ์ฐจ ์ด๋™

const { isLoading, isFetching, data, isError, error, refetch } = useQuery(
  ["super-heroes"],
  getSuperHero,
  {
    onSuccess,
    onError,
    select(data) {
      const superHeroNames = data.data.map((hero: Data) => hero.name);
      return superHeroNames;
    },
  }
);

return (
  <div>
    <button onClick={handleClickRefetch}>Fetch Heroes</button>
    {data.map((heroName: string, idx: number) => (
      <div key={idx}>{heroName}</div>
    ))}
  </div>
);
  • select ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ ํ•จ์ˆ˜์—์„œ ๋ฐ˜ํ™˜๋œ ๋ฐ์ดํ„ฐ์˜ ์ผ๋ถ€๋ฅผ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.

keepPreviousData

๋ชฉ์ฐจ ์ด๋™

const fetchColors = (pageNum: number) => {
  return axios.get(`http://localhost:4000/colors?_limit=2&_page=${pageNum}`);
};

const { isLoading, isError, error, data, isFetching, isPreviousData } =
  useQuery(["colors", pageNum], () => fetchColors(pageNum), {
    keepPreviousData: true,
  });
  • keepPreviousData๋ฅผ true๋กœ ์„ค์ •ํ•˜๋ฉด ์ฟผ๋ฆฌ ํ‚ค๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด์„œ ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๋Š” ๋™์•ˆ์—๋„ ๋งˆ์ง€๋ง‰ data ๊ฐ’์„ ์œ ์ง€ํ•œ๋‹ค.
  • keepPreviousData์€ ํŽ˜์ด์ง€๋„ค์ด์…˜๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ๋•Œ ํŽธ๋ฆฌํ•˜๋‹ค. ์บ์‹ฑ ๋˜์ง€ ์•Š์€ ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ ๋ชฉ๋ก์ด ๊นœ๋นก๊ฑฐ๋ฆฌ๋Š” ํ˜„์ƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋˜ํ•œ, isPreviousData ๊ฐ’์œผ๋กœ ํ˜„์žฌ์˜ ์ฟผ๋ฆฌ ํ‚ค์— ํ•ด๋‹นํ•˜๋Š” ๊ฐ’์ธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ํŽ˜์ด์ง€๋„ค์ด์…˜์„ ์˜ˆ๋กœ ๋“ค๋ฉด, ์•„์ง ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๊ฐ€ ์บ์‹ฑ ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด, ์ด์ „ ๋ฐ์ดํ„ฐ์ด๋ฏ€๋กœ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋ฐ›์•„์ ธ ์™”๋‹ค๋ฉด ์ด์ „๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ false๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

placeholderData

๋ชฉ์ฐจ ์ด๋™

function Todos() {
  const placeholderData = useMemo(() => generateFakeTodos(), []);
  const result = useQuery(["todos"], () => fetch("/todos"), {
    placeholderData,
  });
}
  • placeholderData๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด mock ๋ฐ์ดํ„ฐ ์„ค์ •๋„ ๊ฐ€๋Šฅํ•˜๋‹ค. ๋Œ€์‹  ์บ์‹ฑ์ด ์•ˆ๋œ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

Parallel

๋ชฉ์ฐจ ์ด๋™

const { data: superHeroes } = useQuery(["super-heroes"], fetchSuperHeroes);
const { data: friends } = useQuery(["friends"], fetchFriends);
  • ๋ช‡ ๊ฐ€์ง€ ์ƒํ™ฉ์„ ์ œ์™ธํ•˜๋ฉด ์ฟผ๋ฆฌ ์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ์„ ์–ธ๋œ ์ผ๋ฐ˜์ ์ธ ์ƒํ™ฉ์ผ ๋•Œ, ์ฟผ๋ฆฌ ํ•จ์ˆ˜๋“ค์€ ๊ทธ๋ƒฅ ๋ณ‘๋ ฌ๋กœ ์š”์ฒญ๋ผ์„œ ์ฒ˜๋ฆฌ๋œ๋‹ค.
  • ์ด๋Ÿฌํ•œ ํŠน์ง•์€ ์ฟผ๋ฆฌ ์ฒ˜๋ฆฌ์˜ ๋™์‹œ์„ฑ์„ ๊ทน๋Œ€ํ™” ์‹œํ‚จ๋‹ค.
const queryResults = useQueries(
  heroIds.map((id) => ({
    queryKey: ["super-hero", id],
    queryFn: () => fetchSuperHero(id),
  }))
);
  • ํ•˜์ง€๋งŒ, ์ฟผ๋ฆฌ ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ๋™์‹œ์— ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ๋ Œ๋”๋ง์ด ๊ฑฐ๋“ญ๋˜๋Š” ์‚ฌ์ด์‚ฌ์ด์— ๊ณ„์† ์ฟผ๋ฆฌ๊ฐ€ ์ˆ˜ํ–‰๋˜์–ด์•ผ ํ•œ๋‹ค๋ฉด ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋กœ์ง์ด hook ๊ทœ์น™์— ์–ด๊ธ‹๋‚  ์ˆ˜๋„ ์žˆ๋‹ค. ์ด๋Ÿด ๋•Œ๋Š” useQueries๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.


Dependent Queries

๋ชฉ์ฐจ ์ด๋™

  • ์ข…์† ์ฟผ๋ฆฌ๋Š” ์–ด๋–ค A๋ผ๋Š” ์ฟผ๋ฆฌ๊ฐ€ ์žˆ๋Š”๋ฐ ์ด A์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ์‚ฌ์ „์— ์™„๋ฃŒ๋˜์–ด์•ผ ํ•˜๋Š” B ์ฟผ๋ฆฌ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ด๋Ÿฌํ•œ B์ฟผ๋ฆฌ์— ์˜์กดํ•˜๋Š” A์ฟผ๋ฆฌ๋ฅผ ์ข…์† ์ฟผ๋ฆฌ๋ผ๊ณ  ํ•œ๋‹ค.
  • react-query์—์„œ๋Š” ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๋ ค์ฃผ๋Š” enabled ์˜ต์…˜์„ ํ†ตํ•ด ์ข…์† ์ฟผ๋ฆฌ๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
const DependantQueriesPage = ({ email }: Props) => {
  // ์‚ฌ์ „์— ์™„๋ฃŒ๋˜์–ด์•ผํ•  ์ฟผ๋ฆฌ
  const { data: user } = useQuery(['user', email], () =>
    fetchUserByEmail(email)
  );

  const channelId = user?.data.channelId;

  // user ์ฟผ๋ฆฌ์— ์ข…์† ์ฟผ๋ฆฌ
  const { data } = useQuery(
    ['courses', channelId],
    () => fetchCoursesByChannelId(channelId),
    { enabled: !!channelId }
  );

useQueryClient

๋ชฉ์ฐจ ์ด๋™

  • useQueryClient๋Š” QueryClient ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • QueryClient๋Š” ์บ์‹œ์™€ ์ƒํ˜ธ์ž‘์šฉํ•œ๋‹ค.
  • QueryClient๋Š” ๋‹ค์Œ ๋ฌธ์„œ์—์„œ ์ž์„ธํ•˜๊ฒŒ ๋‹ค๋ฃฌ๋‹ค
import { useQueryClient } from "react-query";

const queryClient = useQueryClient();

Initial Query Data

๋ชฉ์ฐจ ์ด๋™

  • ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•˜๊ธฐ ์ „์— ์บ์‹œ์— ์ œ๊ณตํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.
  • initialData ์˜ต์…˜์„ ํ†ตํ•ด์„œ ์ฟผ๋ฆฌ๋ฅผ ๋ฏธ๋ฆฌ ์ฑ„์šฐ๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ดˆ๊ธฐ ๋กœ๋“œ ์ƒํƒœ๋„ ๊ฑด๋„ˆ๋›ธ ์ˆ˜๋„ ์žˆ๋‹ค.
  const useSuperHeroData = (heroId: string) => {
    const queryClient = useQueryClient();
    return useQuery(['super-hero', heroId], fetchSuperHero, {
      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 };
        return undefined;
      },
    });
  };

  • ์ฐธ๊ณ ๋กœ ์œ„ ์˜ˆ์ œ์—์„œ queryClient.getQueryData ๋ฉ”์„œ๋“œ๋Š” ๊ธฐ์กด ์ฟผ๋ฆฌ์˜ ์บ์‹ฑ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋™๊ธฐ ํ•จ์ˆ˜์ด๋‹ค. ์ฟผ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด undefined๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

Infinite Queries

๋ชฉ์ฐจ ์ด๋™

  • Infinite Queries(๋ฌดํ•œ ์ฟผ๋ฆฌ)๋Š” ๋ฌดํ•œ ์Šคํฌ๋กค์ด๋‚˜ load more(๋” ๋ณด๊ธฐ)๊ณผ ๊ฐ™์ด ํŠน์ • ์กฐ๊ฑด์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€์ ์œผ๋กœ ๋ฐ›์•„์˜ค๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋ฉด ์œ ์šฉํ•˜๋‹ค.
  • react-query๋Š” ์ด๋Ÿฌํ•œ ๋ฌดํ•œ ์ฟผ๋ฆฌ๋ฅผ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด useQuery์˜ ์œ ์šฉํ•œ ๋ฒ„์ „์ธ useInfiniteQuery์„ ์ง€์›ํ•œ๋‹ค.
import { useInfiniteQuery } from "react-query";
// import { useInfiniteQuery } from '@tanstack/react-query' v4

const fetchColors = ({ pageParam = 1 }) => {
  return axios.get(`http://localhost:4000/colors?_limit=2&_page=${pageParam}`);
};

const InfiniteQueries = () => {
  const { data, hasNextPage, isFetching, isFetchingNextPage, fetchNextPage } =
    useInfiniteQuery(["colors"], fetchColors, {
      getNextPageParam: (lastPage, allPages) => {
        return allPages.length < 4 && allPages.length + 1;
      },
    });

  return (
    <div>
      {data?.pages.map((group, idx) => ({
        /* ... */
      }))}
      <div>
        <button disabled={!hasNextPage} onClick={() => fetchNextPage()}>
          LoadMore
        </button>
      </div>
      <div>{isFetching && !isFetchingNextPage ? "Fetching..." : null}</div>
    </div>
  );
};

์ฃผ์š” ๋ฐ˜ํ™˜

  • useInfiniteQuery๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ useQuery์™€ ์‚ฌ์šฉ๋ฒ•์€ ๋น„์Šทํ•˜์ง€๋งŒ, ์ฐจ์ด์ ์ด ์žˆ๋‹ค.
  • useInfiniteQuery๋Š” ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœisFetchingNextPage, isFetchingPreviousPage, fetchNextPage, fetchPreviousPage, hasNextPage ๋“ฑ์ด ์ถ”๊ฐ€์ ์œผ๋กœ ์žˆ๋‹ค.
    • fetchNextPage: ๋‹ค์Œ ํŽ˜์ด์ง€๋ฅผ fetch ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • fetchPreviousPage: ์ด์ „ ํŽ˜์ด์ง€๋ฅผ fetch ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • isFetchingNextPage: fetchNextPage ๋ฉ”์„œ๋“œ๊ฐ€ ๋‹ค์Œ ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋™์•ˆ true์ด๋‹ค.
    • isFetchingPreviousPage: fetchPreviousPage ๋ฉ”์„œ๋“œ๊ฐ€ ์ด์ „ ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋™์•ˆ true์ด๋‹ค.
    • hasNextPage: ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” ๋‹ค์Œ ํŽ˜์ด์ง€๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ true์ด๋‹ค.
    • hasPreviousPage: ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” ์ด์ „ ํŽ˜์ด์ง€๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ true์ด๋‹ค.

์˜ต์…˜

  • pageParam์ด๋ผ๋Š” ํ”„๋กœํผํ‹ฐ๊ฐ€ ์กด์žฌํ•˜๋ฉฐ, queryFn์— ํ• ๋‹นํ•ด์ค˜์•ผ ํ•œ๋‹ค. ์ด๋•Œ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ดˆ๊ธฐ ํŽ˜์ด์ง€ ๊ฐ’์„ ์„ค์ • ํ•ด์ค˜์•ผํ•œ๋‹ค.
  • getNextPageParam์„ ์ด์šฉํ•ด์„œ ํŽ˜์ด์ง€๋ฅผ ์ฆ๊ฐ€์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.
    • getNextPageParam์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ž lastPage๋Š” fetch ํ•ด์˜จ ๊ฐ€์žฅ ์ตœ๊ทผ์— ๊ฐ€์ ธ์˜จ ํŽ˜์ด์ง€ ๋ชฉ๋ก์ด๋‹ค.
    • ๋‘ ๋ฒˆ์งธ ์ธ์ž allPages๋Š” ํ˜„์žฌ๊นŒ์ง€ ๊ฐ€์ ธ์˜จ ๋ชจ๋“  ํŽ˜์ด์ง€ ๋ฐ์ดํ„ฐ์ด๋‹ค.
  • getPreviousPageParam๋„ ์กด์žฌํ•˜๋ฉฐ, getNextPageParam์™€ ๋ฐ˜๋Œ€์˜ ์†์„ฑ์„ ๊ฐ–๊ณ  ์žˆ๋‹ค.

pageParam

  • queryFn์— ๋„˜๊ฒจ์ฃผ๋Š” pageParam๊ฐ€ ๋‹จ์ˆœํžˆ ๋‹ค์Œ page์˜ ๊ฐ’๋งŒ์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.
  • pageParam ๊ฐ’์€ getNextPageParam์—์„œ ์›ํ•˜๋Š” ํ˜•ํƒœ๋กœ ๋ณ€๊ฒฝ์‹œ์ผœ์ค„ ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฌด์Šจ ๋ง์ธ์ง€ ์˜ˆ์‹œ๋ฅผ ๋ณด๋ฉด ์ดํ•ด๊ฐ€ ์‰ฝ๋‹ค. ๐Ÿ‘ ์•„๋ž˜์™€ ๊ฐ™์ด getNextPageParam์—์„œ ๋ฐ˜ํ™˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹จ์ˆœํžˆ ๋‹ค์Œ ํŽ˜์ด์ง€ ๊ฐ’์ด ์•„๋‹Œ ๊ฐ์ฒด๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค๊ณ  ํ•ด๋ณด์ž.
const { data } = useInfiniteQuery(["colors"], fetchColors, {
  getNextPageParam: (lastPage, allPages) => {
    return (
      allPages.length < 4 && {
        page: allPages.length + 1,
        etc: "hi",
      };
    )
  },
});
  • ๊ทธ๋Ÿฌ๋ฉด queryFn์— ๋„ฃ์€ pageParams์—์„œ getNextPageParam์—์„œ ๋ฐ˜ํ™˜ํ•œ ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
/**
 * pageParam
 * { page, etc }
 */
const fetchColors = ({ pageParam }) => {
  const { page = 1, etc } = pageParam;

  return axios.get(`http://localhost:4000/colors?_limit=2&_page=${page}`);
};
  • ์ฆ‰, getNextPageParam์˜ ๋ฐ˜ํ™˜ ๊ฐ’์ด pageParams๋กœ ๋“ค์–ด๊ฐ€๊ธฐ ๋•Œ๋ฌธ์— pageParams๋ฅผ ์›ํ•˜๋Š” ํ˜•ํƒœ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด getNextPageParam์˜ ๋ฐ˜ํ™˜ ๊ฐ’์„ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค.

refetchPage

  • ์ „์ฒด ํŽ˜์ด์ง€ ์ค‘ ์ผ๋ถ€๋งŒ ์ง์ ‘ refetchํ•˜๊ณ  ์‹ถ์„ ๋•Œ์—๋Š”, useInfiniteQuery๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” refetch ํ•จ์ˆ˜์— refetchPage๋ฅผ ๋„˜๊ฒจ์ฃผ๋ฉด ๋œ๋‹ค.
  • refetchPage๋Š” ๊ฐ ํŽ˜์ด์ง€์— ๋Œ€ํ•ด ์‹คํ–‰๋˜๋ฉฐ, ์ด ํ•จ์ˆ˜๊ฐ€ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํŽ˜์ด์ง€๋งŒ refetch๊ฐ€ ๋œ๋‹ค.
const { refetch } = useInfiniteQuery(["colors"], fetchColors, {
  getNextPageParam: (lastPage, allPages) => {
    return allPages.length < 4 && allPages.length + 1;
  },
});

// ์ฒซ๋ฒˆ์งธ ํŽ˜์ด์ง€๋งŒ refetch ํ•ฉ๋‹ˆ๋‹ค.
refetch({ refetchPage: (page, index) => index === 0 });

useMutation mutate

๋ชฉ์ฐจ ์ด๋™

  • useMutation ๊ณต์‹ ์‚ฌ์ดํŠธ
  • react-query์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ Get ํ•  ๋•Œ๋Š” useQuery๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  • ๋งŒ์•ฝ ์„œ๋ฒ„์˜ data๋ฅผ post, patch, put, delete์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด ์ด๋•Œ๋Š” useMutation์„ ์ด์šฉํ•œ๋‹ค.
  • ์š”์•ฝํ•˜์ž๋ฉด R(read)๋Š” useQuery, CUD(Create, Update, Delete)๋Š” useMutation์„ ์‚ฌ์šฉํ•œ๋‹ค.
const CreateTodo = () => {
  const mutation = useMutation(createTodo, {
    onMutate() {},
    onSuccess(data) {
      console.log(data);
    },
    onError(err) {
      console.log(err);
    },
  });

  const onCreateTodo = (e) => {
    e.preventDefault();
    mutation.mutate({ title });
  };

  return <>...</>;
};
  • useMutation์˜ ๋ฐ˜ํ™˜ ๊ฐ’์ธ mutation ๊ฐ์ฒด์˜ mutate ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ์š”์ฒญ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
  • mutate๋Š” onSuccess์™€ onError ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์š”์ฒญ์ด ์„ฑ๊ณตํ–ˆ์„ ์‹œ์™€ ์‹คํŒจํ–ˆ์„ ์‹œ์— ๋Œ€ํ•œ response ๋ฐ์ดํ„ฐ๋ฅผ ํ•ธ๋“ค๋งํ•  ์ˆ˜ ์žˆ๋‹ค.
  • onMutate๋Š” mutation ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „์— ์‹คํ–‰๋˜๊ณ  mutation ํ•จ์ˆ˜๊ฐ€ ๋ฐ›์„ ๋™์ผํ•œ ๋ณ€์ˆ˜๊ฐ€ ์ „๋‹ฌ๋œ๋‹ค.
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๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์–ป์–ด์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
  • Redux์™€ ๊ฐ™์€ Request ์„ฑ๊ณต ์•ก์…˜์„ ๋ฏธ๋“ค์›จ์–ด์—์„œ ํ™•์ธํ•˜์—ฌ ์ถ”๊ฐ€ ์•ก์…˜์„ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ ๊ฐ™์€ ์ž‘์—…์„ ํ•  ๋•Œ๋Š”, mutate๋Š” onSuccess, onError์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— mutateAsync๊ฐ€ ๋” ๊ฐ€๋…์„ฑ์ด ์ข‹๋‹ค

์ฟผ๋ฆฌ ๋ฌดํšจํ™”

๋ชฉ์ฐจ ์ด๋™

  • invalidateQueries์€ ํ™”๋ฉด์„ ์ตœ์‹  ์ƒํƒœ๋กœ ์œ ์ง€ํ•˜๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค๋ฉด, ๊ฒŒ์‹œํŒ ๋ชฉ๋ก์—์„œ ์–ด๋–ค ๊ฒŒ์‹œ๊ธ€์„ ์ž‘์„ฑ(Post)ํ•˜๊ฑฐ๋‚˜ ๊ฒŒ์‹œ๊ธ€์„ ์ œ๊ฑฐ(Delete)ํ–ˆ์„ ๋•Œ ํ™”๋ฉด์— ๋ณด์—ฌ์ฃผ๋Š” ๊ฒŒ์‹œํŒ ๋ชฉ๋ก์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ตœ์‹ ํ™” ํ•ด์•ผํ•  ๋•Œ๊ฐ€ ์žˆ๋‹ค.
  • ํ•˜์ง€๋งŒ ์ด๋•Œ, query Key๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๊ฐ•์ œ๋กœ ์ฟผ๋ฆฌ๋ฅผ ๋ฌดํšจํ™”ํ•˜๊ณ  ์ตœ์‹ ํ™”๋ฅผ ์ง„ํ–‰ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์ด๋Ÿฐ ๊ฒฝ์šฐ์— invalidateQueries() ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ฆ‰, query๊ฐ€ ์˜ค๋ž˜๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์„ ํŒ๋‹จํ•˜๊ณ  ๋‹ค์‹œ refetch๋ฅผ ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค!
import { useMutation, useQuery, useQueryClient } from "react-query";

const useAddSuperHeroData = () => {
  const queryClient = useQueryClient();
  return useMutation(addSuperHero, {
    onSuccess(data) {
      queryClient.invalidateQueries(["super-heroes"]); // ์ด key์— ํ•ด๋‹นํ•˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ ๋ฌดํšจํ™”!
      console.log(data);
    },
    onError(err) {
      console.log(err);
    },
  });
};
  • ๋งŒ์•ฝ ๋ฌดํšจํ™” ํ•˜๋ ค๋Š” ํ‚ค๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ๋ผ๋ฉด ์•„๋ž˜ ์˜ˆ์ œ์™€ ๊ฐ™์ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐฐ์—ด๋กœ ๋ณด๋‚ด์ฃผ๋ฉด ๋œ๋‹ค.
queryClient.invalidateQueries(["super-heroes", "posts", "comment"]);
  • ์œ„์— enabled/refetch์—์„œ๋„ ์–ธ๊ธ‰ํ–ˆ์ง€๋งŒ enabled: false ์˜ต์…˜์„ ์ฃผ๋ฉดqueryClient๊ฐ€ ์ฟผ๋ฆฌ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ• ์ค‘ invalidateQueries์™€ refetchQueries๋ฅผ ๋ฌด์‹œํ•œ๋‹ค.
  • ์ž์„ธํ•œ ๋‚ด์šฉ์€ queryClient.invalidateQueries ์ •๋ฆฌ๋ฅผ ์ฐธ๊ณ ํ•˜์ž.

์บ์‹œ ๋ฐ์ดํ„ฐ ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธ

๋ชฉ์ฐจ ์ด๋™

  • ๋ฐ”๋กœ ์œ„์—์„œ queryClient.invalidateQueries๋ฅผ ์ด์šฉํ•ด ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ์ตœ์‹ ํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ดค๋Š”๋ฐ queryClient.setQueryData๋ฅผ ์ด์šฉํ•ด์„œ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋‹ค.
  • queryClient.setQueryData๋Š” ์ฟผ๋ฆฌ์˜ ์บ์‹œ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋™๊ธฐ ํ•จ์ˆ˜์ด๋‹ค.
const useAddSuperHeroData = () => {
  const queryClient = useQueryClient();
  return useMutation(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) ์ธก๋ฉด์—์„œ ์ข‹๋‹ค.
const useAddSuperHeroData = () => {
  const queryClient = useQueryClient();
  return useMutation(addSuperHero, {
    async onMutate(newHero) {
      // ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ๋ฅผ ๋ฎ์–ด์“ฐ์ง€ ์•Š๊ธฐ ์œ„ํ•ด ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์‚ญ์ œํ•œ๋‹ค.
      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);
    },
    // ์˜ค๋ฅ˜ ๋˜๋Š” ์„ฑ๊ณต ํ›„์—๋Š” ํ•ญ์ƒ ๋ฆฌํ”„๋ ˆ์‰ฌ
    onSettled() {
      queryClient.invalidateQueries(["super-heroes"]);
    },
  });
};
  • ์ฐธ๊ณ ๋กœ ์œ„ ์˜ˆ์ œ์—์„œ cancelQueries๋Š” ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ทจ์†Œ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. ์ทจ์†Œ์‹œํ‚ฌ query์˜ queryKey๋ฅผ cancelQueries์˜ ์ธ์ž๋กœ ๋ณด๋‚ด ์‹คํ–‰์‹œํ‚จ๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค์–ด, ์š”์ฒญ์„ ์™„๋ฃŒํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๊ฒฝ์šฐ, ์‚ฌ์šฉ์ž๊ฐ€ ์ทจ์†Œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ์š”์ฒญ์„ ์ค‘์ง€ํ•˜๋Š” ๊ฒฝ์šฐ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

useQueryErrorResetBoundary

๋ชฉ์ฐจ ์ด๋™

  • useQueryErrorResetBoundary ๊ณต์‹ ๋ฌธ์„œ
  • react-query์—์„œ๋Š” Error๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ๋Š” useQueryErrorResetBoundary hook์„ ์ œ๊ณตํ•œ๋‹ค.
  • ์ž์„ธํ•œ ๋‚ด์šฉ์„ ์„ค๋ช…ํ•˜๊ธฐ๋ณด๋‹ค๋Š” ๊ฐ„๋‹จํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๊ณ , ์‘์šฉํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค.
  • ์šฐ์„ , useQueryErrorResetBoundary๋Š” ErrorBoundary์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋Š”๋ฐ ์ด๋Š”, react-error-boundary ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
npm i react-error-boundary
๋˜๋Š”
yarn add react-error-boundary
  • ์„ค์น˜ ํ›„์— ์•„๋ž˜์™€ ๊ฐ™์€ QueryErrorBoundary๋ผ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๊ทธ ๋‚ด๋ถ€์— useQueryErrorResetBoundary ํ›…์„ ํ˜ธ์ถœํ•ด reset ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.
  • ์•„๋ž˜ ์ฝ”๋“œ ๋‚ด์šฉ์€ ๋‹จ์ˆœํ•˜๋‹ค.
    • Error๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ErrorBoundary์˜ fallbackRender prop์œผ๋กœ ๋„˜๊ธด ๋‚ด์šฉ์ด ๋ Œ๋”๋ง ๋˜๊ณ , ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด children ๋‚ด์šฉ์ด ๋ Œ๋”๋ง ๋œ๋‹ค.
    • ๋˜ํ•œ, fallbackRender์— ๋„ฃ์–ด์ฃผ๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ resetErrorBoundary๋ฅผ ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹น์„ ํ†ตํ•ด ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๋ฅผ ํ†ตํ•ด ๋ชจ๋“  ์ฟผ๋ฆฌ ์—๋Ÿฌ๋ฅผ ์ดˆ๊ธฐํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ ๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” button์„ ํด๋ฆญํ•˜๋ฉด ์—๋Ÿฌ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ฒŒ๋” ์ž‘์„ฑํ–ˆ๋‹ค.
import { useQueryErrorResetBoundary } from "react-query"; // (*)
import { ErrorBoundary } from "react-error-boundary"; // (*)

interface Props {
  children: React.ReactNode;
}

const QueryErrorBoundary = ({ children }: Props) => {
  const { reset } = useQueryErrorResetBoundary(); // (*)

  return (
    <ErrorBoundary
      onReset={reset}
      fallbackRender={({ resetErrorBoundary }) => (
        <div>
          Error!!
          <button onClick={() => resetErrorBoundary()}>Try again</button>
        </div>
      )}
    >
      {children}
    </ErrorBoundary>
  );
};

export default QueryErrorBoundary;
  • ๊ทธ๋ฆฌ๊ณ  App.js์—๋‹ค QueryErrorBoundary ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค. ์—ฌ๊ธฐ์„œ ์ฃผ์˜ ํ•  ์ ์€ queryClient ์˜ต์…˜์—๋‹ค { useErrorBoundary: true }๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. ๊ทธ๋ž˜์•ผ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ErrorBoundary ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
import { QueryClientProvider, QueryClient } from "react-query";
import QueryErrorBoundary from "./components/ErrorBoundary"; // (*)

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      useErrorBoundary: true, // (*)
    },
  },
});

export const AppContext = createContext < any > undefined;

function App() {
  return (
    <AppContext.Provider value={user}>
      <QueryClientProvider client={queryClient}>
        <QueryErrorBoundary>{/* code */}</QueryErrorBoundary>
      </QueryClientProvider>
    </AppContext.Provider>
  );
}

export default App;
  • ์ถ”๊ฐ€์ ์œผ๋กœ useQueryErrorResetBoundary hook์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  QueryErrorResetBoundary ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์„œ Error๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ๋„ ์žˆ๋Š”๋ฐ, ๊ฒฐ๊ตญ ์ปจ์…‰์€ ๋น„์Šทํ•ด์„œ ์•„๋ž˜ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ์ถฉ๋ถ„ํžˆ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.
  • QueryErrorResetBoundary

React Query Typescript

๋ชฉ์ฐจ ์ด๋™

  • React Query๋Š” TypeScript์˜ ์ œ๋„ค๋ฆญ(Generics)์„ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค. ์ด๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค์ง€ ์•Š๊ณ  API๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐ์ดํ„ฐ ์œ ํ˜•์„ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
  • ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ๊ทธ๋‹ค์ง€ ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ๋‹ค๋ฃจ์ง€๋Š” ์•Š๊ณ , useQuery๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๊ธฐ๋Œ€ํ•˜๋Š” ์ œ๋„ค๋ฆญ์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•˜๋„๋ก ์•Œ๋ ค์ค€๋‹ค.

useQuery

  • ํ˜„์žฌ useQuery๊ฐ€ ๊ฐ–๊ณ  ์žˆ๋Š” ์ œ๋„ค๋ฆญ์€ 4๊ฐœ์ด๋ฉฐ, ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
    1. TQueryFnData: useQuery๋กœ ์‹คํ–‰ํ•˜๋Š” query function์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ์˜ ํƒ€์ž…์„ ์ง€์ •ํ•˜๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค.
    2. TError: query function์˜ error ํ˜•์‹์„ ์ •ํ•˜๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค.
    3. TData: useQuery์˜ data์— ๋‹ด๊ธฐ๋Š” ์‹ค์งˆ์ ์ธ ๋ฐ์ดํ„ฐ์˜ ํƒ€์ž…์„ ๋งํ•œ๋‹ค. ์ฒซ ๋ฒˆ์งธ ์ œ๋„ค๋ฆญ๊ณผ์˜ ์ฐจ์ด์ ์€ select์™€ ๊ฐ™์ด query function์˜ ๋ฐ˜ํ™˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ ํ•ธ๋“ค๋ง์„ ํ†ตํ•ด ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ์— ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ์ข‹๋‹ค.
    4. TQueryKey: useQuery์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ ์ฃผ๋Š” queryKey์˜ ํƒ€์ž…์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•ด์ฃผ๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค.
// useQuery์˜ ํƒ€์ž…
export function useQuery<
  TQueryFnData = unknown,
  TError = unknown,
  TData = TQueryFnData,
  TQueryKey extends QueryKey = QueryKey
>
// useQuery ํƒ€์ž… ์ ์šฉ ์˜ˆ์‹œ
const { data } = useQuery<
  SuperHeros,
  AxiosError,
  SuperHeroName[],
  [string, number]
>(["super-heros", id], getSuperHero, {
  select: (data) => {
    const superHeroNames = data.data.map((hero) => hero.name);
    return superHeroNames;
  },
});
  • data: SuperHeroName[]
  • error: AxiosError<any, any>
  • select: (data: SuperHeros): SuperHeroName[]

useMutation

  • useMutation๋„ useQuery์™€ ๋™์ผํ•˜๊ฒŒ ํ˜„์žฌ 4๊ฐœ์ด๋ฉฐ, ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
    1. TData: useMutaion์— ๋„˜๊ฒจ์ค€ mutation function์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ์˜ ํƒ€์ž…์„ ์ง€์ •ํ•˜๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด๋‹ค.
      • data์˜ ํƒ€์ž…๊ณผ onSuccess(1๋ฒˆ์งธ ์ธ์ž)์˜ ์ธ์ž์˜ ํƒ€์ž…์œผ๋กœ ํ™œ์šฉ๋œ๋‹ค.
    2. TError: useMutaion์— ๋„˜๊ฒจ์ค€ 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๋ฒˆ์งธ ์ธ์ž)์—์„œ ํ™œ์šฉํ•˜๋ ค๋ฉด ํ•ด๋‹น ํƒ€์ž…์„ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค.
export function useMutaion<
  TData = unknown,
  TError = unknown,
  TVariables = void,
  TContext = unknown
>
// useMutation ํƒ€์ž… ์ ์šฉ ์˜ˆ์‹œ
const { mutate } = useMutation<Todo, AxiosError, number, number>(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<any, any>
  • 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),

react-query-tutorial's People

Contributors

ssi02014 avatar okinawaa avatar k-kbk avatar

Stargazers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.