- 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
- ์๋ฒ์ Http CUD๊ด๋ จ ์์ ์ ์ํ useMutation๊ณผ mutate
- ์ฟผ๋ฆฌ ๋ฌดํจํ queryClient.invalidateQueries
- ์บ์ ๋ฐ์ดํฐ ์ฆ์ ์ ๋ฐ์ดํธ๋ฅผ ์ํ queryClient.setQueryData
- ์ฌ์ฉ์UX๋ฅผ ์ฌ๋ ค์ฃผ๋ Optimistic Updates(๋๊ด์ ์ ๋ฐ์ดํธ)
- 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๋ ๊ตฌ์ฑ ์์๋ฅผ ์ฌ์ฉํ์ฌ 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์ด๋ฉด ๊ฐ๋ฐ ๋๊ตฌ๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์ด๋ ค ์๋๋ก ์ค์ ํ ์ ์๋ค. - panelProps (PropsObject): ํจ๋์
props์ ์ถ๊ฐํ ์ ์๋ค. ์๋ฅผ ๋ค์ด className, style, onClick ๋ฑ - closeButtonProps( PropsObject): ๋ซ๊ธฐ ๋ฒํผ์ props๋ฅผ ์ถ๊ฐํ ์ ์๋ค.
- toggleButtonProps (PropsObject): ํ ๊ธ ๋ฒํผ์ props๋ฅผ ์ถ๊ฐํ ์ ์๋ค.
- position?: ("top-left" | "top-right" | "bottom-left" | "bottom-right")
- ๊ธฐ๋ณธ๊ฐ:
bottom-left - devtools ํจ๋์ ์ด๊ณ ๋ซ๊ธฐ ์ํ ๋ก๊ณ ์์น
- ๊ธฐ๋ณธ๊ฐ:
- React-Query ์บ์ ๋ผ์ดํ ์ฌ์ดํด
* Query Instances with and without cache data(์บ์ ๋ฐ์ดํฐ๊ฐ ์๊ฑฐ๋ ์๋ ์ฟผ๋ฆฌ ์ธ์คํด์ค)
* Background Refetching(๋ฐฑ๊ทธ๋ผ์ด๋ ๋ฆฌํจ์นญ)
* Inactive Queries(๋นํ์ฑ ์ฟผ๋ฆฌ)
* Garbage Collection(๊ฐ๋น์ง ์ปฌ๋ ์
)
- A ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ
mount๋จ - ๋คํธ์ํฌ์์ ๋ฐ์ดํฐ
fetchํ๊ณ A๋ผ๋query key๋ก ์บ์ฑํจ - ์ด ๋ฐ์ดํฐ๋
fresh์ํ์์staleTime(๊ธฐ๋ณธ๊ฐ 0)์ดํstale์ํ๋ก ๋ณ๊ฒฝ๋จ - A ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ
unmount๋จ - ์บ์๋
cacheTime(๊ธฐ๋ณธ๊ฐ 5min)๋งํผ ์ ์ง๋๋ค๊ฐ๊ฐ๋น์ง ์ฝ๋ ํฐ๋ก ์์ง๋จ - ๋ง์ผ cacheTime์ด ์ง๋๊ธฐ ์ ์ด๊ณ , ๋ฐ์ดํฐ๊ฐ stale ์ํ์ผ ๊ฒฝ์ฐ A ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ ์๋กญ๊ฒ mount๋๋ฉด, fetch๊ฐ ์คํ๋๊ณ freshํ ๊ฐ์ ๊ฐ์ ธ์ค๋ ๋์ ์บ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค
// ์ฌ์ฉ๋ฒ(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 { isLoading, data } = useQuery("super-heroes", getSuperHero);- useQuery๋ ๊ธฐ๋ณธ์ ์ผ๋ก 3๊ฐ์ ์ธ์๋ฅผ ๋ฐ์ต๋๋ค. ์ฒซ ๋ฒ์งธ ์ธ์๊ฐ
queryKey(ํ์), ๋ ๋ฒ์งธ ์ธ์๊ฐqueryFn(ํ์), ์ธ ๋ฒ์งธ ์ธ์๊ฐoptions์ ๋๋ค. - useQuery๋ ์ฒซ ๋ฒ์งธ ์ธ์์ธ
queryKey๋ฅผ ๊ธฐ๋ฐ์ผ๋ก๋ฐ์ดํฐ ์บ์ฑ์ ๊ด๋ฆฌํฉ๋๋ค.๋ฌธ์์ด๋๋๋ฐฐ์ด๋ก ์ง์ ํ ์ ์๋๋ฐ, ์ผ๋ฐ์ ์ผ๋ก๋ ์ ์์ ์ฒ๋ผ๋ฌธ์์ด๋ก ์ง์ ํ ์ ์์ง๋ง, ๋ง์ฝ ์ฟผ๋ฆฌ๊ฐ ํน์ ๋ณ์์ ์์กดํ๋ ๊ฒฝ์ฐ์๋ ์๋ ์์ ์ฒ๋ผ๋ฐฐ์ด๋ก ์ง์ ํด ํด๋น ๋ณ์๋ฅผ ์ถ๊ฐํด์ฃผ์ด์ผ ํฉ๋๋ค. - ์ฌ์ฉ๋ฒ
(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 = () => {
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 ๊ณต์ ์ฌ์ดํธ ์ฐธ๊ณ
- status: ์ฟผ๋ฆฌ ์์ฒญ ํจ์์ ์ํ๋ฅผ ํํํ๋ status๋ 4๊ฐ์ง์ ๊ฐ์ด ์กด์ฌํ๋ค.(๋ฌธ์์ด ํํ)
- idle: ์ฟผ๋ฆฌ ๋ฐ์ดํฐ๊ฐ ์๊ณ ๋น์์ ๋, { enabled: false } ์ํ๋ก ์ฟผ๋ฆฌ๊ฐ ํธ์ถ๋๋ฉด ์ด ์ํ๋ก ์์๋๋ค.
- loading: ๋ง ๊ทธ๋๋ก ๋ก๋ฉ์ค์ผ ๋ ์ํ
- error: ์๋ฌ ๋ฐ์ํ์ ๋ ์ํ
- success: ์์ฒญ ์ฑ๊ณตํ์ ๋ ์ํ
- data: ์ฟผ๋ฆฌ ํจ์๊ฐ ๋ฆฌํดํ Promise์์
resolved๋ ๋ฐ์ดํฐ - isLoading:
์บ์ฑ๋ ๋ฐ์ดํฐ๊ฐ ์์๋!fetch ๊ณผ์ ์ค์ true ์ฆ, ์บ์ฑ ๋ฐ์ดํฐ๊ฐ ์์ผ๋ฉด false - isFetching: ๋ฐ์ดํฐ๊ฐ
fetch๋ ๋ false์์ true๊ฐ ๋๋ค. ์บ์ฑ ๋ฐ์ดํฐ๊ฐ ์์ด์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ fetch ๋๋๋ผ๋ true์ด๋ค. - error: ์ฟผ๋ฆฌ ํจ์๊ฐ ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ์ ๋ํ ์ค๋ฅ ๊ฐ์ฒด
- isError: ์๋ฌ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ true
- ๊ทธ์ธ ๋ฆฌํด ๋ฐ์ดํฐ๋ค์ ํ์ธํ๊ณ ์ถ์ผ๋ฉด ์๋จ์ ๊ณต์ ์ฌ์ดํธ ์ฐธ๊ณ
const { isLoading, isError, error, data, isFetching } = useQuery(
["colors", pageNum],
() => fetchColors(pageNum)
);- useQuery ๊ณต์ ์ฌ์ดํธ ์ฐธ๊ณ
- ์๋ ์์ ๋ค ์ ์ธํ๊ณ ์ถ๊ฐ์ ์ธ ์ต์ ๋ค์ ์ ์ฌ์ดํธ ์ฐธ๊ณ
- stale์ ์ฉ์ด ๋ป๋๋ก
์ฉ์์ด๋ผ๋ ์๋ฏธ์ด๋ค. ์ฆ, ์ต์ ์ํ๊ฐ ์๋๋ผ๋ ์๋ฏธ์ด๋ค. - fresh๋ ๋ป ๊ทธ๋๋ก
์ ์ ํ์ด๋ผ๋ ์๋ฏธ์ด๋ค. ์ฆ, ์ต์ ์ํ๋ผ๋ ์๋ฏธ์ด๋ค.
const { isLoading, isFetching, data, isError, error } = useQuery(
"super-heroes",
getSuperHero,
{
cacheTime: 3000,
staleTime: 50000,
}
);- staleTime
- staleTime์ ๋ฐ์ดํฐ๊ฐ
fresh์์ stale์ํ๋ก ๋ณ๊ฒฝ๋๋๋ฐ ๊ฑธ๋ฆฌ๋ ์๊ฐ, ๋ง์ฝ staleTime์ด 3000์ด๋ฉด fresh์ํ์์ 3์ด๋ค์ stale๋ก ๋ณํ fresh์ํ์ผ๋๋ ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ ์๋กญ๊ฒ mount ๋์ด๋ ๋คํธ์ํฌ ์์ฒญ(fetch)์ด ์ผ์ด๋์ง ์๋๋ค.- ๋ฐ์ดํฐ๊ฐ ํ๋ฒ fetch ๋๊ณ ๋์
staleTime์ด ์ง๋์ง ์์๋ค๋ฉด(fresh์ํ) unmount ํ mount ๋์ด๋ fetch๊ฐ ์ผ์ด๋์ง ์๋๋ค. - staleTime์ ๊ธฐ๋ณธ๊ฐ์
0์ด๊ธฐ ๋๋ฌธ์ ์ผ๋ฐ์ ์ผ๋ก fetch ํ์ ๋ฐ๋ก stale์ด ๋๋ค.
- staleTime์ ๋ฐ์ดํฐ๊ฐ
- cacheTime
- ๋ฐ์ดํฐ๊ฐ
inactive์ํ์ผ ๋์บ์ฑ๋ ์ํ๋ก๋จ์์๋ ์๊ฐ - ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ unmount ๋๋ฉด ๋ฐ์ดํฐ๋
inactive ์ํ๋ก ๋ณ๊ฒฝ๋๋ฉฐ, ์บ์๋cacheTime๋งํผ ์ ์ง๋๋ค. - cacheTime์ด ์ง๋๋ฉด
๊ฐ๋น์ง ์ฝ๋ ํฐ๋ก ์์ง๋๋ค. - cacheTime์ด ์ง๋๊ธฐ ์ ์ ์ฟผ๋ฆฌ ์ธ์คํด์ค๊ฐ ๋ค์ mount ๋๋ฉด, ๋ฐ์ดํฐ๋ฅผ fetchํ๋ ๋์ ์บ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค๋ค.
- cacheTime์ staleTime๊ณผ ๊ด๊ณ์์ด, ๋ฌด์กฐ๊ฑด inactive ๋ ์์ ์ ๊ธฐ์ค์ผ๋ก ์บ์ ๋ฐ์ดํฐ ์ญ์ ๋ฅผ ๊ฒฐ์ ํ๋ค.
- cacheTime์ ๊ธฐ๋ณธ๊ฐ์
5๋ถ
- ๋ฐ์ดํฐ๊ฐ
const { isLoading, isFetching, data, isError, error } = useQuery(
"super-heroes",
getSuperHero,
{
refetchOnMount: true,
}
);- refetchOnMount๋ ๋ฐ์ดํฐ๊ฐ
stale์ํ์ผ ๊ฒฝ์ฐ ๋ง์ดํธ ์ ๋ง๋คrefetch๋ฅผ ์คํํ๋ ์ต์ ์ด๋ค. - ๊ธฐ๋ณธ๊ฐ์
true์ธ๋ฐ ์ด๊ฒ ๋ฒ ์คํธ๋ค. 'always'๋ก ์ค์ ํ๋ฉด ๋ง์ดํธ ์ ๋ง๋ค ๋งค๋ฒ refetch๋ฅผ ์คํํ๋ค.false๋ก ์ค์ ํ๋ฉด ์ต์ด fetch ์ดํ์๋ refetchํ์ง ์๋๋ค.
const { isLoading, isFetching, data, isError, error } = useQuery(
"super-heroes",
getSuperHero,
{
refetchOnWindowFocus: true,
}
);- refetchOnWindowFocus๋ ๋ฐ์ดํฐ๊ฐ
stale์ํ์ผ ๊ฒฝ์ฐ์๋์ฐ ํฌ์ปค์ฑ๋ ๋ ๋ง๋ค refetch๋ฅผ ์คํํ๋ ์ต์ ์ด๋ค. - ์๋ฅผ ๋ค์ด, ํฌ๋กฌ์์ ๋ค๋ฅธ ํญ์ ๋๋ ๋ค๊ฐ ๋ค์ ์๋ ๋ณด๋ ์ค์ธ ํญ์ ๋๋ ์ ๋๋ ์ด ๊ฒฝ์ฐ์ ํด๋นํ๋ค. ์ฌ์ง์ด F12๋ก ๊ฐ๋ฐ์ ๋๊ตฌ ์ฐฝ์ ์ผ์ ๋คํธ์ํฌ ํญ์ด๋ , ์ฝ์ ํญ์ด๋ ๊ฐ๋ฐ์ ๋๊ตฌ ์ฐฝ์์ ๋๋ค๊ฐ ํ์ด์ง ๋ด๋ถ๋ฅผ ๋ค์ ํด๋ฆญํ์ ๋๋ ์ด ๊ฒฝ์ฐ์ ํด๋นํ๋ค.
- ๊ธฐ๋ณธ๊ฐ์
true์ด๋ค. always๋ก ์ค์ ํ๋ฉด ํญ์ ์๋์ฐ ํฌ์ปค์ฑ ๋ ๋ ๋ง๋ค refetch๋ฅผ ์คํํ๋ค๋ ์๋ฏธ์ด๋ค.
const { isLoading, isFetching, data, isError, error } = useQuery(
"super-heroes",
getSuperHero,
{
// refetchInterval: 2000,
refetchIntervalInBackground: true,
}
);- ํด๋ง์ด๋? ๋ฆฌ์ผํ์ ์น์ ์ํ ๊ธฐ๋ฒ์ผ๋ก
์ผ์ ํ ์ฃผ๊ธฐ(ํน์ ํ ์๊ฐ)๋ฅผ ๊ฐ์ง๊ณ ์๋ฒ์ ์๋ต์ ์ฃผ๊ณ ๋ฐ๋ ๋ฐฉ์์ด ํด๋ง ๋ฐฉ์์ด๋ค. - react-query์์๋
refetchInterval์ ์ด์ฉํด์ ๊ตฌํํ ์ ์๋ค. refetchIntervalInBackground์ผ๋ก๋ ํด๋ง์ ๊ตฌํํ ์ ์๋๋ฐrefetchIntervalํญ/์ฐฝ์ด ๋ฐฑ๊ทธ๋ผ์ด๋์ ์๋ ๋์ ๊ณ์ ๋ค์ ๊ฐ์ ธ์ต๋๋ค.
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๋ ์ฟผ๋ฆฌ๊ฐ ์คํจํ๋ฉด 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๊ฐ์ผ๋ก ํ์ฌ์ ์ฟผ๋ฆฌ ํค์ ํด๋นํ๋ ๊ฐ์ธ์ง ํ์ธํ ์ ์๋ค.
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๋ฃฐ์ ์๋ฐฐ๋ ์๋ ์๋ค. ์ด๋ด ๋๋
uesQueries๋ฅผ ์ฌ์ฉํ๋ค.
์ข ์ ์ฟผ๋ฆฌ๋ ์ด๋ค 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๋ ์บ์์ ์ํธ์์ฉ ํ๋ค.
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๋ฅผ ๋ฐํํ๋ค.
- ๋ฌดํ ์ฟผ๋ฆฌ๋ ๋ฌดํ ์คํฌ๋กค์ด๋ load more ๊ฐ์ด ํน์ ์กฐ๊ฑด์์ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐ์ ์ผ๋ก ๋ฐ์์ค๋ ๊ธฐ๋ฅ ๊ตฌํํ ๋ ์ฌ์ฉํ๋ฉด ์ ์ฉํ๋ค.
import { useInfiniteQuery } from "react-query";
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) => {
if (allPages.length < 4) {
return allPages.length + 1;
} else {
return undefined;
}
},
});
return (
<div>
{data?.pages.map((group, idx) => (
<Fragment key={idx}>
{group.data.map((color: any) => (
<h2 key={color.id}>
{color.id}. {color.label}
</h2>
))}
</Fragment>
))}
<div>
<button disabled={!hasNextPage} onClick={() => fetchNextPage()}>
LoadMore
</button>
</div>
<div>{isFetching && !isFetchingNextPage ? "Fetching..." : null}</div>
</div>
);
};Returns
useInfiniteQuery๋ ๊ธฐ๋ณธ์ ์ผ๋ก useQuery์ ์ฌ์ฉ๋ฒ์ ๋น์ทํ์ง๋ง ์ฐจ์ด์ ์ด ์๋ค.- useInfiniteQuery๋ ๋ฐํ๊ฐ์ผ๋ก isFetchingNextPage, isFetchingPreviousPage, fetchNextPage, fetchPreviousPage ๋ฑ์ด ์ถ๊ฐ์ ์ผ๋ก ์๋ค.
fetchNextPage๋ฅผ ํธ์ถํ๋ฉด ๋ค์ ํ์ด์ง๋ฅผ fetch ํ ์ ์๋ค.fetchPreviousPage๋ฅผ ํธ์ถํ๋ฉด ์ด์ ํ์ด์ง๋ฅผ fetch ํ ์ ์๋ค.isFetchingNextPage์fetchNextPage๋ฉ์๋๊ฐ ๋ค์ ํ์ด์ง๋ฅผ ๊ฐ์ ธ์ค๋ ๋์ true์ด๋ค. ์ฆ, ์ด๊ธฐ๊ฐ์ true์ด๊ณ , ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ฉด false๊ฐ ๋๋ค.isFetchingPreviousPage์fetchPreviousPage๋ฉ์๋๊ฐ ์ด์ ํ์ด์ง๋ฅผ ๊ฐ์ ธ์ค๋ ๋์ true์ด๋ค. ์ฆ, ์ด๊ธฐ๊ฐ์ true์ด๊ณ , ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ฉด false๊ฐ ๋๋ค.hasNextPage๋ ๊ฐ์ ธ์ฌ ์ ์๋ ๋ค์ ํ์ด์ง๊ฐ ์์ ๊ฒฝ์ฐ true์ด๋ค.
์ต์
pageParam์ด๋ผ๋ ํ๋กํผํฐ๊ฐ ์กด์ฌํ๋ฉฐ queryFn์ ํ ๋นํด์ค์ผํ๋ค. ์ด๋ ๊ธฐ๋ณธ๊ฐ์1๋ก ํด์ค์ผํ๋ค.- ๊ทธ๋ฆฌ๊ณ
getNextPageParam์ ์ด์ฉํด์ ํ์ด์ง๋ฅผ ์ฆ๊ฐ์ํจ๋ค.- ์ด๋, getNextPageParam์ ์ฒซ ๋ฒ์งธ ์ธ์
lastPage๋ fetch ํด์จ ๊ฐ์ฅ ์ต๊ทผ์ ๊ฐ์ ธ์จ ํ์ด์ง ๋ชฉ๋ก์ด๋ค. - ๋ ๋ฒ์งธ ์ธ์
allPages๋ ํ์ฌ๊น์ง ๊ฐ์ ธ์จ ๋ชจ๋ ํ์ด์ง๋ค ๋ฐ์ดํฐ์ด๋ค.
- ์ด๋, getNextPageParam์ ์ฒซ ๋ฒ์งธ ์ธ์
getPreviousPageParam๋ ์กด์ฌํ๋ฉฐ,getNextPageParam์ ๋ฐ๋์ ์์ฑ์ ๊ฐ๊ณ ์๋ค.- ๊ทธ๋ฆฌ๊ณ ์์ฒญ์ด ์ฑ๊ณตํ๊ณ ๋ฐํ๋๋ data๋
pages๋ผ๋ ํ๋กํผํฐ๋ฅผ ๊ฐ๊ณ ์์ผ๋ฉฐ, pages๋group์ด๋ผ๋ ํ๋กํผํฐ๋ฅผ ๊ฐ๊ณ ์๋ค.
- 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๊ฐ ๋ ๊ฐ๋ ์ฑ์ด ์ข๋ค
- ์ด๊ฒ์ ๊ฐ๋ ์ ์ผ๋ก ํ๋ฉด์ ์ต์ ์ํ๋ก ์ ์งํ๋ ๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ด๋ค..
- ์๋ฅผ ๋ค๋ฉด, ๊ฒ์ํ ๋ชฉ๋ก์์ ์ด๋ค ๊ฒ์๊ธ์
์์ฑ(Post)ํ๊ฑฐ๋ ๊ฒ์๊ธ์์ ๊ฑฐ(Delete)ํ์ ๋ ํ๋ฉด์ ๋ณด์ฌ์ฃผ๋ ๊ฒ์ํ ๋ชฉ๋ก์ ์ค์๊ฐ ์ต์ ํ ํด์ผ๋ ๋๊ฐ ์๋ค. - ํ์ง๋ง ์ด๋,
query Key๊ฐ ๋ณํ์ง ์์ผ๋ฏ๋ก ์ด๋ด๋ ๊ฐ์ ๋ฆฌํ๋ ์ฌ๋ฅผ ์งํํด์ผ ํ๋๋ฐ ์ด๋,queryClient์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"]);- ๋ฐ๋ก ์์์
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) {
// ๋๊ด์ ์
๋ฐ์ดํธ๋ฅผ ๋ฎ์ด์ฐ์ง ์๊ธฐ ์ํด Cancelํ๋ค.
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์ key๋ฅผ cancelQueries์ ์ธ์๋ก ๋ณด๋ด ์คํ์ํจ๋ค.
- cancelQueries๊ฐ ์คํ๋๋ฉด ์ฒ์ query์์ ๋ฐํํ promise ๊ฐ์ฒด์ cancel์ ์๋์ผ๋ก ํธ์ถํ๋ค๊ณ ํ๋ค.
