// hooks/items.hooks.ts
import { useQuery } from "react-query";
export enum ServerStateKeysEnum {
Items = 'items'
}
데이터 계층과 프레젠테이션 계층을 분리하여 관심사를 명확히 구분할 수 있습니다.
이 말은 즉, API fetch function을 React 컴포넌트에서 가져올 필요가 없다는것을 의미합니다.
// hooks/items.hooks.ts
import { useQuery } from "react-query";
export enum ServerStateKeysEnum {
Items = 'items'
}
export const useGetItems = () =>
useQuery(
ServerStateKeysEnum.Items,
() => fetch('https://example.com/feedbacks'), //Simple fetch function
);
// components/SomeComponent.tsx
import React from "react";
import { useGetItems } from "hooks/items.hooks.ts";
export const SomeComponent: React.FC = () => {
const { data: items } = useGetItems();
return <>{ items }</>
}
그리고 이렇게 custom hook으로 만들면 해당 데이터를 props drilling 할 필요가 없어집니다. 자식 컴포넌트에 props로 데이터를 넘길 필요 없이 각 컴포넌트에서 custom hook을 불러오면 됩니다.(stale time을 잘 활용하면 불필요한 api 호출 막을 수 있음)
// components/SomeComponent.tsx
import React from "react";
import { useGetItems } from "hooks/items.hooks.ts";
export const SomeComponent: React.FC = () => {
const { data: items } = useGetItems();
return (<>
<h1>You have {items.length} items</h1>
{/* 이것은 item에 관심이 없는 일부 중간 구성 요소이며
item에 관심이 있는 손자 구성 요소에 도달하기 위해 데이터를 전달할 필요가 없습니다.
*/}
<SomeRandomChildComponent />
</>);
}
// components/SomeGreatGrandChildrenComponen.tsx
import React from "react";
import { useGetItems } from "hooks/items.hooks.ts";
export const SomeGreatGrandChildrenComponen: React.FC = () => {
const { data: items } = useGetItems();
return (<>{items}</>);
}
위에서 잠깐 언급한 stale time을 잘 활용하면 불필요한 api 호출 막을 수 있음
은 아래의 config로 지정할 수 있습니다. 해당 옵션은 프로젝트 전체의 option, 각 useQuery의 option으로 모두 지정할 수 있습니다.
// app.tsx
import { QueryClient, QueryClientProvider } from 'react-query';
const queryCache = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
retry: false,
staleTime: 30000,
},
},
});
const App = () => (
<QueryClientProvider client={queryCache}>
<FirstSiblingComponent />
<SecondSiblingComponent />
</QueryClientProvider>
);
만약 products list를 수정할 수 있는 어드민 페이지가 있다고 가정해봅시다.
처음 api에서 products를 불러온 후 staleTime을 infinity로 지정한 후 수정 및 추가 작업을 한 후에만 다시 가져오도록 하고 싶을땐 어떻게 해야 할까요? 바로 react query의 invalidateQueries 매서드를 활용하면 됩니다.
// hooks/items.hooks.ts
import { useMutation, useQueryClient } from "react-query";
import {postCreateNewItem} from './items.api';
import { useQuery } from "react-query";
export enum ServerStateKeysEnum {
Items = 'items'
}
export const useGetItems = () =>
useQuery(
ServerStateKeysEnum.Items,
() => fetch('https://example.com/feedbacks'), //Simple fetch function
);
export const useCreateItem = () => {
const cache = useQueryClient();
return useMutation(postCreateNewItem, {
onSuccess: () => {
cache.invalidateQueries(ServerStateKeysEnum.Items);
}
});
};
custom hook으로 useQury, useMutation을 작성하여 활용하려고 하는데 각 컴포넌트마다 옵션이 다른경우가 존재합니다.
만약 useQuery의 경우, 특정 상황에서는 해당 api를 요청하지 않아도 되는 경우 enabled 옵션을 컴포넌트에서 정해야 할 수 있습니다.
// hooks/items.hooks.ts
import { useQuery, UseQueryOptions } from "react-query";
export enum ServerStateKeysEnum {
Items = 'items'
}
export const useGetItems = (options?: UseQueryOptions) =>
useQuery(
ServerStateKeysEnum.Items,
() => fetch('https://example.com/feedbacks'), //Simple fetch function
{
...options
}
);
// components/SomeComponent.tsx
export const SomeComponent: React.FC<{ hasValidSubscription: boolean }> = ({
hasValidSubscription
}) => {
const { data: items } = useGetItems({
enabled: hasValidSubscription //If hasValidSubscription === false, query won't be executed
});
return (<>{items}</>);
}
useMutation의 경우 만약 에러처리, 성공처리가 모든 컴포넌트에서 같다면 custom option이 불 필요하지만 만약 컴포넌트마다 에러처리, 성공처리를 다르게 해주어야 한다면 custom option으로 지정해줄 수 있습니다.
// hooks/items.hooks.ts
import { useMutation } from "react-query";
import {patchItem} from './items.api';
import { useRenderToastMessage } from '../some-helper-hooks/useRenderToastMessage';
export const useMutateItem = (options?: UseMutationOptions) => {
const toast = useRenderToastMessage();
return useMutation(patchItem, {
onSuccess: () => {
toast.render({
theme: 'success',
message: 'Item successfully modified'
});
},
onError: () => {
toast.notify({
theme: 'error',
children: 'Could not modify item'
});
},
});
};
// components/SomeComponent.tsx
export const SomeComponent: React.FC = () => {
const { mutate } = useMutateItem({
onSuccess: () => {
toast.render({
theme: 'success',
message: 'Item successfully modified'
});
},
onError: () => {
toast.notify({
theme: 'error',
children: 'Could not modify item'
});
},
});
const { data } = mutate();
return (<>{data}</>);
}
staleTime: 30000, // 30초 동안 캐시된 데이터를 사용
queryInvalidationInterval: 60000, // 1분마다 서버에 재요청하여 캐시 갱신
vs 소켓