- ํด๋น ์ ์ฅ์๋ react-query์์ ์์ฃผ ์ฌ์ฉํ๋ ๊ฐ๋ ๋ค์ ์ ๋ฆฌํ ์ ์ฅ์์ ๋๋ค. react-query์ ๋ชจ๋ ํ์ฉ ๋ฐฉ๋ฒ์ด ์์ฑ๋ ์ํ๋ ์๋๋ฉฐ, ํ์ํ ๋ด์ฉ์ ์ถ๊ฐ, ๋ณด์ํ ์์ ์ ๋๋ค.
- ์คํ์ ๋ฐ ๊ฐ๋
์ฑ์ด ์ ์ข๊ฑฐ๋ ์์ ์ด ํ์ํ ๋ด์ฉ์
Pull Request
,Issue
๋ฑ ์์ ๋กญ๊ฒ ๋จ๊ฒจ์ฃผ์๋ฉด ๊ฒํ ํ์ ๋ฐ์ํ๊ฒ ์ต๋๋ค. ๋ง๊ด๋ถ ๐โโ๏ธ
- react-query v4๊ฐ ์ ์ ๋ฆด๋ฆฌ์ฆ๋๋ฉด์ ์ฃผ์ ๋ณ๊ฒฝ ์ฌํญ์ ์๋ ๋ฌธ์์ ์ถ๊ฐํ๊ณ ์์ต๋๋ค.
- react-query v3 vs v4 ๋น๊ต ๋ฌธ์
- React-Query ๊ธฐ๋ฅ
- ๊ธฐ๋ณธ ์ค์ (QueryClientProvider, QueryClient)
- React Query Devtools
- React Query ์บ์ฑ ๋ผ์ดํ ์ฌ์ดํด
- useQuery
- useQuery ์ฃผ์ ๋ฆฌํด ๋ฐ์ดํฐ
- staleTime๊ณผ cacheTime
- ๋ง์ดํธ ๋ ๋๋ง๋ค ์ฌ์์ฒญํ๋ refetchOnMount
- ์๋์ฐ๊ฐ ํฌ์ปค์ฑ ๋ ๋๋ง๋ค ์ฌ์์ฒญํ๋ refetchOnWindowFocus
- Polling ๋ฐฉ์์ ๊ตฌํํ๊ธฐ ์ํ refetchInterval์ refetchIntervalInBackground)
- ์๋ ์คํ์ enabled์ ์๋์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ๋ค์ ์์ฒญํ๋ refetch
- ์คํจํ ์ฟผ๋ฆฌ์ ๋ํด ์ฌ์์ฒญํ๋ retry
- onSuccess, onError, onSettled Callback
- select๋ฅผ ์ด์ฉํ ๋ฐ์ดํฐ ๋ณํ
- Paginated ๊ตฌํ์ ์ ์ฉํ keepPreviousData
- ์ฟผ๋ฆฌ๋ฅผ ๋ณ๋ ฌ(Parallel) ์์ฒญํ ์ ์๋ useQueries
- ์ข ์ ์ฟผ๋ฆฌ(Dependent Queries)
- QueryClient ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ useQueryClient
- ์ด๊ธฐ ๋ฐ์ดํฐ๋ฅผ ์ค์ ํ ์ ์๋ initialData
- Infinite Queries(๋ฌดํ ์ฟผ๋ฆฌ) + useInfiniteQuery
- ์๋ฒ์ HTTP CUD๊ด๋ จ ์์ ์ ์ํ useMutation๊ณผ mutate
- ์ฟผ๋ฆฌ๋ฅผ ๋ฌดํจํํ ์ ์๋ queryClient.invalidateQueries
- ์บ์ ๋ฐ์ดํฐ ์ฆ์ ์ ๋ฐ์ดํธ๋ฅผ ์ํ queryClient.setQueryData
- ์ฌ์ฉ์ ๊ฒฝํ(UX)์ ์ฌ๋ ค์ฃผ๋ Optimistic Updates(๋๊ด์ ์ ๋ฐ์ดํธ)
- ์๋ฌ ์ฒ๋ฆฌ๋ฅผ ์ํ useQueryErrorResetBoundary
- ๋ฆฌ์กํธ ์ฟผ๋ฆฌ์ ํ์ ์คํฌ๋ฆฝํธ ์ ์ฉ
- react-query๋ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์
์์
์๋ฒ ์ํ ๊ฐ์ ธ์ค๊ธฐ
,์บ์ฑ
,๋๊ธฐํ ๋ฐ ์ ๋ฐ์ดํธ
๋ฅผ ๋ณด๋ค ์ฝ๊ฒ ๋ค๋ฃฐ ์ ์๋๋ก ๋์์ฃผ๋ฉฐ ํด๋ผ์ด์ธํธ ์ํ์ ์๋ฒ ์ํ๋ฅผ ๋ช ํํ ๊ตฌ๋ถํ๊ธฐ ์ํด์ ๋ง๋ค์ด์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. - react-query์์ ๊ธฐ์กด ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
(redux, mobX)
๋ํด๋ผ์ด์ธํธ ์ํ ์์
์ ์ ํฉํ์ง๋ง๋น๋๊ธฐ ๋๋ ์๋ฒ ์ํ ์์
์๋ ๊ทธ๋ค์ง ์ข์ง ์๋ค๊ณ ๋งํ๊ณ ์๋ค. - ํด๋ผ์ด์ธํธ ์ํ(Client State)์ ์๋ฒ ์ํ(Server State)๋ ์์ ํ ๋ค๋ฅด๋ฉฐ, ํด๋ผ์ด์ธํธ ์ํ๋ ์ปดํฌ๋ํธ์์ ๊ด๋ฆฌํ๋ ๊ฐ๊ฐ์ input ๊ฐ์ผ๋ก ์๋ฅผ ๋ค ์ ์๊ณ ์๋ฒ ์ํ๋ database์ ์ ์ฅ๋์ด์๋ ๋ฐ์ดํฐ๋ก ์๋ฅผ ๋ค ์ ์๋ค.
-
์๋
- React Query์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์์น์ ๋ฐ์ดํฐ๊ฐ ์ผ๋ง๋ ํ์ํ์ง ์๋ ค์ฃผ๋ฉด ๋๋จธ์ง๋ ์๋์ด๋ค. React Query๋ ๊ตฌ์ฑ์ด ํ์ ์๋ ์ฆ์
์บ์ฑ
,๋ฐฑ๊ทธ๋ผ์ด๋ ์ ๋ฐ์ดํธ
๋ฐ ์ค๋๋ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค.
- React Query์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์์น์ ๋ฐ์ดํฐ๊ฐ ์ผ๋ง๋ ํ์ํ์ง ์๋ ค์ฃผ๋ฉด ๋๋จธ์ง๋ ์๋์ด๋ค. React Query๋ ๊ตฌ์ฑ์ด ํ์ ์๋ ์ฆ์
-
์น์ํ๊ณ ๊ฐ๋จํ๋ค.
promise
๋๋async/await
๋ก ์์ ํ๋ ๋ฐฉ๋ฒ์ ์๊ณ ์๋ค๋ฉด ์ด๋ฏธ React Query๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์๊ณ ์๋ ๊ฒ์ด๋ค. ๊ด๋ฆฌํ ์ ์ญ ์ํ, ๊ฐ์๊ธฐ, ์ ๊ทํ ์์คํ ๋๋ ์ดํดํด์ผ ํ ๋ฌด๊ฑฐ์ด ๊ตฌ์ฑ์ด ์๋ค. ๋ฐ์ดํฐ๋ฅผ ํด๊ฒฐํ๋(๋๋ ์ค๋ฅ๋ฅผ ๋ฐ์์ํค๋) ํจ์๋ฅผ ์ ๋ฌํ๊ธฐ๋ง ํ๋ฉด ๋๋ค.
-
๊ฐ๋ ฅํ ๋๊ตฌ ๋ฐ ๊ตฌ์ฑ
- React Query๋ ๋ชจ๋ ์ฌ์ฉ ์ฌ๋ก์ ๋ง๋ ๋
ธ๋ธ์ ์ต์
์ ์ฌ์ฉํ์ฌ ์ฟผ๋ฆฌ์ ๊ฐ ๊ด์ฐฐ์ ์ธ์คํด์ค๊น์ง ๊ตฌ์ฑํ ์ ์๋ค.
์ ์ฉ devtools
,๋ฌดํ ๋ก๋ฉ API
๋ฐ๋ฐ์ดํฐ ์ ๋ฐ์ดํธ
๋ฅผ ์ฝ๊ฒ ๋ง๋ค์ด์ฃผ๋ mutation ๋๊ตฌ๊ฐ ์๋ค.
- 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๋ฅผ ์ฐ๊ฒฐํ๊ณ ์ ๊ณตํ๋ค.
- 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๋ถ์ ๊ฐ์
A
๋ผ๋ queryKey๋ฅผ ๊ฐ์ง A ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐmount
๋จ- ๋คํธ์ํฌ์์ ๋ฐ์ดํฐ fetchํ๊ณ , ๋ถ๋ฌ์จ ๋ฐ์ดํฐ๋ A๋ผ๋ queryKey๋ก
์บ์ฑ
ํจ - ์ด ๋ฐ์ดํฐ๋
fresh
์ํ์์staleTime(๊ธฐ๋ณธ๊ฐ 0)
์ดํstale
์ํ๋ก ๋ณ๊ฒฝ๋จ - A ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ
unmount
๋จ - ์บ์๋
cacheTime(๊ธฐ๋ณธ๊ฐ 5min)
๋งํผ ์ ์ง๋๋ค๊ฐ๊ฐ๋น์ง ์ฝ๋ ํฐ(GC)
๋ก ์์ง๋จ - ๋ง์ผ, cacheTime์ด ์ง๋๊ธฐ ์ ์ด๊ณ , A ์ฟผ๋ฆฌ ์ธ์คํด์ค freshํ ์ํ๋ผ๋ฉด ์๋กญ๊ฒ mount๋๋ฉด ์บ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค๋ค.
// ์ฌ์ฉ๋ฒ(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
๋ถํฐ๋ ๋ฌด์กฐ๊ฑด๋ฐฐ์ด
๋ก ์ง์ ํด์ผ ํฉ๋๋ค.
- ์ฐธ๊ณ ๋ก ํด๋น ๋ด์ฉ์ v3๊ธฐ์ค์ด๊ณ , react-query
์ฌ์ฉ๋ฒ (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);
},
});
};
const { status, isLoading, isError, error, data, isFetching, ... } = useQuery(
["colors", pageNum],
() => fetchColors(pageNum)
);
- status: ์ฟผ๋ฆฌ ์์ฒญ ํจ์์ ์ํ๋ฅผ ํํํ๋ status๋ 4๊ฐ์ง์ ๊ฐ์ด ์กด์ฌํ๋ค.(๋ฌธ์์ด ํํ)
- idle: ์ฟผ๋ฆฌ ๋ฐ์ดํฐ๊ฐ ์๊ณ ๋น์์ ๋, { enabled: false } ์ํ๋ก ์ฟผ๋ฆฌ๊ฐ ํธ์ถ๋๋ฉด ์ด ์ํ๋ก ์์๋๋ค.
์ฐธ๊ณ ๋ก v4๋ถํฐ๋ idle ์ํ๋ ์ ๊ฑฐ๋๋ค.
- loading: ๋ง ๊ทธ๋๋ก ์์ง ์บ์๋ ๋ฐ์ดํฐ๊ฐ ์๊ณ ๋ก๋ฉ์ค์ผ ๋ ์ํ
- error: ์์ฒญ ์๋ฌ ๋ฐ์ํ์ ๋ ์ํ
- success: ์์ฒญ ์ฑ๊ณตํ์ ๋ ์ํ
- idle: ์ฟผ๋ฆฌ ๋ฐ์ดํฐ๊ฐ ์๊ณ ๋น์์ ๋, { enabled: false } ์ํ๋ก ์ฟผ๋ฆฌ๊ฐ ํธ์ถ๋๋ฉด ์ด ์ํ๋ก ์์๋๋ค.
- data: ์ฟผ๋ฆฌ ํจ์๊ฐ ๋ฆฌํดํ Promise์์
resolved
๋ ๋ฐ์ดํฐ - isLoading:
์บ์ฑ ๋ ๋ฐ์ดํฐ๊ฐ ์์ ๋!
์ฆ, ์ฒ์ ์คํ๋ ์ฟผ๋ฆฌ ์ผ ๋ ๋ก๋ฉ ์ฌ๋ถ์ ๋ฐ๋ผ true/false๋ก ๋ฐํ๋๋ค.- ์ด๋ ์บ์ฑ ๋ ๋ฐ์ดํฐ๊ฐ ์๋ค๋ฉด ๋ก๋ฉ ์ฌ๋ถ์ ์๊ด์์ด false๋ฅผ ๋ฐํํ๋ค.
- isFetching: ์บ์ฑ ๋ ๋ฐ์ดํฐ๊ฐ ์๋๋ผ๋ ์ฟผ๋ฆฌ๊ฐ ์คํ๋๋ฉด ๋ก๋ฉ ์ฌ๋ถ์ ๋ฐ๋ผ true/false๋ก ๋ฐํ๋๋ค.
- ์ด๋ ์บ์ฑ ๋ ๋ฐ์ดํฐ๊ฐ ์๋๋ผ๋ ์ฟผ๋ฆฌ ๋ก๋ฉ ์ฌ๋ถ์ ๋ฐ๋ผ true/false๋ฅผ ๋ฐํํ๋ค.
- error: ์ฟผ๋ฆฌ ํจ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ, ์ฟผ๋ฆฌ์ ๋ํ ์ค๋ฅ ๊ฐ์ฒด
- isError: ์๋ฌ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ
true
- ๊ทธ ์ธ ๋ฆฌํด ๋ฐ์ดํฐ๋ค์ ์์ธํ ์๊ณ ์ถ์ผ๋ฉด ๊ณต์ ์ฌ์ดํธ ์ฐธ๊ณ
- useQuery ๊ณต์ ์ฌ์ดํธ ์ฐธ๊ณ
- ์๋ ์์ ๋ค ์ ์ธํ๊ณ ์ถ๊ฐ์ ์ธ ์ต์ ๋ค์ ์ ์ฌ์ดํธ ์ฐธ๊ณ
- stale์ ์ฉ์ด ๋ป๋๋ก
์ฉ์
์ด๋ผ๋ ์๋ฏธ์ด๋ค. ์ฆ, ์ต์ ์ํ๊ฐ ์๋๋ผ๋ ์๋ฏธ์ด๋ค. - fresh๋ ๋ป ๊ทธ๋๋ก
์ ์ ํ
์ด๋ผ๋ ์๋ฏธ์ด๋ค. ์ฆ, ์ต์ ์ํ๋ผ๋ ์๋ฏธ์ด๋ค.
const { isLoading, isFetching, data, isError, error } = useQuery(
["super-heroes"],
getSuperHero,
{
cacheTime: 3000,
staleTime: 50000,
}
);
- staleTime (number | Infinity)
- staleTime์ ๋ฐ์ดํฐ๊ฐ
fresh์์ stale
์ํ๋ก ๋ณ๊ฒฝ๋๋ ๋ฐ ๊ฑธ๋ฆฌ๋ ์๊ฐ, ๋ง์ฝ staleTime์ด 3000์ด๋ฉด fresh์ํ์์ 3์ด ๋ค์ stale๋ก ๋ณํ fresh
์ํ์ผ ๋๋ ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ ์๋กญ๊ฒ mount ๋์ด๋ ๋คํธ์ํฌ ์์ฒญ(fetch)์ด ์ผ์ด๋์ง ์๋๋ค.- ๋ฐ์ดํฐ๊ฐ ํ๋ฒ fetch ๋๊ณ ๋์
staleTime
์ด ์ง๋์ง ์์๋ค๋ฉด(fresh์ํ) unmount ํ ๋ค์ mount ๋์ด๋ fetch๊ฐ ์ผ์ด๋์ง ์๋๋ค. - staleTime์ ๊ธฐ๋ณธ๊ฐ์
0
์ด๊ธฐ ๋๋ฌธ์ ์ผ๋ฐ์ ์ผ๋ก fetch ํ์ ๋ฐ๋ก stale์ด ๋๋ค.
- staleTime์ ๋ฐ์ดํฐ๊ฐ
- cacheTime (number | Infinity)
- ๋ฐ์ดํฐ๊ฐ
inactive
์ํ์ผ ๋์บ์ฑ ๋ ์ํ๋ก
๋จ์์๋ ์๊ฐ - ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ unmount ๋๋ฉด ๋ฐ์ดํฐ๋
inactive ์ํ๋ก ๋ณ๊ฒฝ
๋๋ฉฐ, ์บ์๋cacheTime
๋งํผ ์ ์ง๋๋ค. - cacheTime์ด ์ง๋๋ฉด
๊ฐ๋น์ง ์ฝ๋ ํฐ
๋ก ์์ง๋๋ค. - cacheTime์ด ์ง๋๊ธฐ ์ ์ ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ ๋ค์ mount ๋๋ฉด, ๋ฐ์ดํฐ๋ฅผ fetchํ๋ ๋์ ์บ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค๋ค.
- cacheTime์ staleTime๊ณผ ๊ด๊ณ์์ด, ๋ฌด์กฐ๊ฑด inactive ๋ ์์ ์ ๊ธฐ์ค์ผ๋ก ์บ์ ๋ฐ์ดํฐ ์ญ์ ๋ฅผ ๊ฒฐ์ ํ๋ค.
- cacheTime์ ๊ธฐ๋ณธ๊ฐ์
5๋ถ
์ด๋ค.
- ๋ฐ์ดํฐ๊ฐ
- ์ฌ๊ธฐ์ ์ฃผ์ํ ์ ์ staleTime๊ณผ cacheTime์ ๊ธฐ๋ณธ๊ฐ์ ๊ฐ๊ฐ
0๋ถ
๊ณผ5๋ถ
์ด๋ค. ๋ฐ๋ผ์ staleTime์ ์ด๋ ํ ์ค์ ๋ ํ์ง ์์ผ๋ฉด ์บ์ฑ์ด ์ ํ ๋์ง ์๋๋ค. ์๋ํ๋ฉด, ํญ์ ์บ์ฑ ๋์ด ์๋ ๋ฐ์ดํฐ๊ฐstale
ํ๋ค๊ณ ์ฌ๊ธฐ๊ธฐ ๋๋ฌธ์ด๋ค. - staleTime์ ๊ธธ๊ฒ ์ค์ ํ๋๋ผ๋ cacheTime์ด ์งง๋ค๋ฉด ์ด ๋ํ ์บ์ฑ์ด ์ํํ๊ฒ ์งํ๋์ง ์์ ๊ฒ์ด๋ค. ๊ฒฐ๊ตญ์๋ ๋ ๊ฐ์ ์ต์ ์ ์ ์ ํ๊ฒ ์ค์ ํด์ค์ผ ํ๋ค.
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ํ์ง ์๋๋ค.
const { isLoading, isFetching, data, isError, error } = useQuery(
"super-heroes",
getSuperHero,
{
refetchOnWindowFocus: true,
}
);
- refetchOnWindowFocus (boolean | "always")
- refetchOnWindowFocus๋ ๋ฐ์ดํฐ๊ฐ
stale
์ํ์ผ ๊ฒฝ์ฐ์๋์ฐ ํฌ์ปค์ฑ
๋ ๋๋ง๋ค refetch๋ฅผ ์คํํ๋ ์ต์ ์ด๋ค. ๊ธฐ๋ณธ๊ฐ์true
์ด๋ค. - ์๋ฅผ ๋ค์ด, ํฌ๋กฌ์์ ๋ค๋ฅธ ํญ์ ๋๋ ๋ค๊ฐ ๋ค์ ์๋ ๋ณด๋ ์ค์ธ ํญ์ ๋๋ ์ ๋๋ ์ด ๊ฒฝ์ฐ์ ํด๋นํ๋ค. ์ฌ์ง์ด F12๋ก ๊ฐ๋ฐ์ ๋๊ตฌ ์ฐฝ์ ์ผ์ ๋คํธ์ํฌ ํญ์ด๋ , ์ฝ์ ํญ์ด๋ ๊ฐ๋ฐ์ ๋๊ตฌ ์ฐฝ์์ ๋๋ค๊ฐ ํ์ด์ง ๋ด๋ถ๋ฅผ ๋ค์ ํด๋ฆญํ์ ๋๋ ์ด ๊ฒฝ์ฐ์ ํด๋นํ๋ค.
always
๋ก ์ค์ ํ๋ฉด ํญ์ ์๋์ฐ ํฌ์ปค์ฑ ๋ ๋๋ง๋ค refetch๋ฅผ ์คํํ๋ค๋ ์๋ฏธ์ด๋ค.
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๋ฅผ ์์ผ์ฃผ๋ ๊ฒ์ ์๋ฏธํ๋ค.
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
๋ฅผ ๋ฌด์ํ๋ค.
const result = useQuery(["todos", 1], fetchTodoListPage, {
retry: 10, // ์ค๋ฅ๋ฅผ ํ์ํ๊ธฐ ์ ์ ์คํจํ ์์ฒญ์ 10๋ฒ ์ฌ์๋ํฉ๋๋ค.
});
- retry (boolean | number | (failureCount: number, error: TError) => boolean)
- retry๋ ์ฟผ๋ฆฌ๊ฐ
์คํจ
ํ๋ฉด useQuery๋ฅผํน์ ํ์(๊ธฐ๋ณธ๊ฐ 3)
๋งํผ ์ฌ์์ฒญํ๋ ์ต์ ์ด๋ค. - retry๊ฐ
false
์ธ ๊ฒฝ์ฐ, ์คํจํ ์ฟผ๋ฆฌ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ค์ ์๋ํ์ง ์๋๋ค. true
์ธ ๊ฒฝ์ฐ์๋ ์คํจํ ์ฟผ๋ฆฌ์ ๋ํด์ ๋ฌดํ ์ฌ์์ฒญ์ ์๋ํ๋ค.- ๊ฐ์ผ๋ก
์ซ์
๋ฅผ ๋ฃ์ ๊ฒฝ์ฐ, ์คํจํ ์ฟผ๋ฆฌ๊ฐ ํด๋น ์ซ์๋ฅผ ์ถฉ์กฑํ ๋๊น์ง ์์ฒญ์ ์ฌ์๋ํ๋ค.
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
ํจ์๋ ์ฟผ๋ฆฌ ์์ฒญ์ด ์ฑ๊ณต, ์คํจ ๋ชจ๋ ์คํ๋๋ค.
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
์ต์ ์ ์ฌ์ฉํ์ฌ ์ฟผ๋ฆฌ ํจ์์์ ๋ฐํ๋ ๋ฐ์ดํฐ์ ์ผ๋ถ๋ฅผ ๋ณํํ๊ฑฐ๋ ์ ํํ ์ ์๋ค.
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๋ฅผ ๋ฐํํ๋ค.
function Todos() {
const placeholderData = useMemo(() => generateFakeTodos(), []);
const result = useQuery(["todos"], () => fetch("/todos"), {
placeholderData,
});
}
- placeholderData๋ฅผ ์ฌ์ฉํ๋ฉด
mock ๋ฐ์ดํฐ
์ค์ ๋ ๊ฐ๋ฅํ๋ค. ๋์ ์บ์ฑ์ด ์๋๋ค๋ ๋จ์ ์ด ์๋ค.
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
๋ฅผ ์ฌ์ฉํ๋ค.
์ข ์ ์ฟผ๋ฆฌ
๋ ์ด๋ค 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๋
QueryClient
์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ค. QueryClient
๋ ์บ์์ ์ํธ์์ฉํ๋ค.- QueryClient๋ ๋ค์ ๋ฌธ์์์ ์์ธํ๊ฒ ๋ค๋ฃฌ๋ค
import { useQueryClient } from "react-query";
const queryClient = useQueryClient();
- ์ฟผ๋ฆฌ์ ๋ํ
์ด๊ธฐ ๋ฐ์ดํฐ
๊ฐ ํ์ํ๊ธฐ ์ ์ ์บ์์ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ์ด ์๋ค. - 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(๋ฌดํ ์ฟผ๋ฆฌ)๋
๋ฌดํ ์คํฌ๋กค
์ด๋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์ด๋ค.
- fetchNextPage:
์ต์
pageParam
์ด๋ผ๋ ํ๋กํผํฐ๊ฐ ์กด์ฌํ๋ฉฐ,queryFn
์ ํ ๋นํด์ค์ผ ํ๋ค. ์ด๋ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ด๊ธฐ ํ์ด์ง ๊ฐ์ ์ค์ ํด์ค์ผํ๋ค.getNextPageParam
์ ์ด์ฉํด์ ํ์ด์ง๋ฅผ ์ฆ๊ฐ์ํฌ ์ ์๋ค.- getNextPageParam์ ์ฒซ ๋ฒ์งธ ์ธ์
lastPage
๋ fetch ํด์จ ๊ฐ์ฅ ์ต๊ทผ์ ๊ฐ์ ธ์จ ํ์ด์ง ๋ชฉ๋ก์ด๋ค. - ๋ ๋ฒ์งธ ์ธ์
allPages
๋ ํ์ฌ๊น์ง ๊ฐ์ ธ์จ ๋ชจ๋ ํ์ด์ง ๋ฐ์ดํฐ์ด๋ค.
- getNextPageParam์ ์ฒซ ๋ฒ์งธ ์ธ์
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 ๊ณต์ ์ฌ์ดํธ
- 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(๋๊ด์ ์ ๋ฐ์ดํธ)
๋ ์๋ฒ ์ ๋ฐ์ดํธ ์ 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 ๊ณต์ ๋ฌธ์
- 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์ ํด๋ฆญํ๋ฉด ์๋ฌ๋ฅผ ์ด๊ธฐํํ๊ฒ๋ ์์ฑํ๋ค.
- Error๊ฐ ๋ฐ์ํ๋ฉด ErrorBoundary์
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์
์ ๋ค๋ฆญ(Generics)
์ ๋ง์ด ์ฌ์ฉํ๋ค. ์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ค์ ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค์ง ์๊ณ API๊ฐ ๋ฐํํ๋ ๋ฐ์ดํฐ ์ ํ์ ์ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. - ๊ณต์ ๋ฌธ์์์๋ ํ์ ์คํฌ๋ฆฝํธ๋ฅผ ๊ทธ๋ค์ง ๊ด๋ฒ์ํ๊ฒ ๋ค๋ฃจ์ง๋ ์๊ณ , useQuery๋ฅผ ํธ์ถํ ๋ ๊ธฐ๋ํ๋ ์ ๋ค๋ฆญ์ ๋ช ์์ ์ผ๋ก ์ง์ ํ๋๋ก ์๋ ค์ค๋ค.
- ํ์ฌ useQuery๊ฐ ๊ฐ๊ณ ์๋ ์ ๋ค๋ฆญ์
4๊ฐ
์ด๋ฉฐ, ๋ค์๊ณผ ๊ฐ๋ค.- TQueryFnData: useQuery๋ก ์คํํ๋ query function์
์คํ ๊ฒฐ๊ณผ
์ ํ์ ์ ์ง์ ํ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค. - TError: query function์
error
ํ์์ ์ ํ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค. - TData: useQuery์
data์ ๋ด๊ธฐ๋ ์ค์ง์ ์ธ ๋ฐ์ดํฐ
์ ํ์ ์ ๋งํ๋ค. ์ฒซ ๋ฒ์งธ ์ ๋ค๋ฆญ๊ณผ์ ์ฐจ์ด์ ์select
์ ๊ฐ์ด query function์ ๋ฐํ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐ ํธ๋ค๋ง์ ํตํด ๋ฐํํ๋ ๊ฒฝ์ฐ์ ๋์ํ ์ ์๋ ํ์ ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ์ข๋ค. - TQueryKey: useQuery์ ์ฒซ ๋ฒ์งธ ์ธ์๋ก ์ฃผ๋
queryKey
์ ํ์ ์ ๋ช ์์ ์ผ๋ก ์ง์ ํด์ฃผ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค.
- TQueryFnData: useQuery๋ก ์คํํ๋ query function์
// 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๋ useQuery์ ๋์ผํ๊ฒ ํ์ฌ 4๊ฐ์ด๋ฉฐ, ๋ค์๊ณผ ๊ฐ๋ค.
- TData: useMutaion์ ๋๊ฒจ์ค mutation function์
์คํ ๊ฒฐ๊ณผ
์ ํ์ ์ ์ง์ ํ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค.- data์ ํ์ ๊ณผ onSuccess(1๋ฒ์งธ ์ธ์)์ ์ธ์์ ํ์ ์ผ๋ก ํ์ฉ๋๋ค.
- TError: useMutaion์ ๋๊ฒจ์ค mutation function์
error
ํ์์ ์ ํ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค. - TVariables:
mutate ํจ์
์ ์ ๋ฌ ํ ์ธ์๋ฅผ ์ง์ ํ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค.- onSuccess(2๋ฒ์งธ ์ธ์), onError(2๋ฒ์งธ ์ธ์), onMutate(1๋ฒ์งธ ์ธ์), onSettled(3๋ฒ์งธ ์ธ์) ์ธ์์ ํ์ ์ผ๋ก ํ์ฉ๋๋ค.
- TContext: mutation function์ ์คํํ๊ธฐ ์ ์ ์ํํ๋
onMutate ํจ์์ return๊ฐ
์ ์ง์ ํ๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ค.- onMutate์ ๊ฒฐ๊ณผ ๊ฐ์ ํ์ ์ onSuccess(3๋ฒ์งธ ์ธ์), onError(3๋ฒ์งธ ์ธ์), onSettled(4๋ฒ์งธ ์ธ์)์์ ํ์ฉํ๋ ค๋ฉด ํด๋น ํ์ ์ ์ง์ ํด์ผ ํ๋ค.
- TData: useMutaion์ ๋๊ฒจ์ค mutation function์
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)
,