1) 설치
npm install @tanstack/react-query
2) queryClient 생성 ( _app.js )
export default function MyApp({ Component, pageProps }) {
// 순수 react 의 경우에는 이렇게 선언
// const queryClient = new QueryClient();
const [queryClient] = useState(()=> new QueryClient({
// 옵션 추가 가능
defaultOptions:{
queries:{
retry:1,
retryDelay: 0,
staleTime: 60 * 1000
}
}
}));
return (
<QueryClientProvider client={queryClient}>
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
</Hydrate>
</QueryClientProvider>
)
}
[ queryClient를 useState로 선언하는 이유 ]
Next.js의 경우 페이지를 이동할 때 _app.js부터 새롭게 렌더링 시키기 때문에 useState를 이용해 한번만 선언되게 만들어준다. 이렇게 하지 않으면 새로운 queryClient가 생성되면서 기존 데이터가 유실될 수 있다.
QueryClientProvider는 ContextProvider로 동작하며 하위 컴포넌트에서 QueryClient를 사용할 수 있게 해준다. ( 캐시 데이터 사용 가능)
[ Prefetching data with React Query ]
서버사이드에서 useQuery를 사용해 데이터를 prefetching 하는 방법에는 두 가지가 있다.
export async function getStaticProps() {
const posts = await getPosts()
return { props: { posts } }
}
function Posts(props) {
const { data } = useQuery(['posts'], getPosts, { initialData: props.posts })
// ...
}
서버사이드에서 prefetch한 쿼리를 queryClient에 dehydrate 하는 방식이다. (SSG, SSR 동일)
export async function getStaticProps() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery(['posts'], getPosts)
return {
props: {
dehydratedState: dehydrate(queryClient),
},
}
}
function Posts() {
// This useQuery could just as well happen in some deeper child to
// the "Posts"-page, data will be available immediately either way
const { data } = useQuery(['posts'], getPosts)
// This query was not prefetched on the server and will not start
// fetching until on the client, both patterns are fine to mix
const { data: otherData } = useQuery(['posts-2'], getPosts)
// ...
}
// memberApi.js
export const getMemberList = (param) => {
return useQuery(["memberLists", param], ()=>getMemberListFetch(param),{
...queryOption,
onSuccess: data => {
// 성공 시 호출
},
onError: error => {
// 실패 시 호출
}
})
}
export const getMemberListFetch = async (param) => {
const data = await request.get(PATH_ADMIN_API.MEMBER.LIST(param)).catch((error) => {
throw error
})
return data
}
// member.js
export default function MemberInfoTable() {
const {data:memberInfoLists} = getMemberList(param)
return ...
}
const { data: todoList, error, isFetching } = useQuery("todos", fetchTodoList);
const { data: nextTodo, error, isFetching } = useQuery(
"nextTodos",
fetchNextTodoList,
{
enabled: !!todoList // true가 되면 fetchNextTodoList를 실행한다
}
);
const info = {
memberId: "2"
};
const result = useQueries([
{
queryKey: ["memberInfo", info.memberId],
queryFn: params => {
console.log(params); // {queryKey: ['memberInfo', '2'], pageParam: undefined, meta: undefined}
return api.getRunInfo(riot.version);
}
},
{
queryKey: ["getSpell", riot.version],
queryFn: () => api.getSpellInfo(riot.version)
}
]);
const queryCache = queryClient.getQueryCache();
const query = queryCache.find({queryKey})
const query = queryCache.findAll({queryKey})
const queryCache = queryClient.getQueryCache();
const callback = event =>{
// 에러처리 함수
console.log('error: ', event?.query?.state?.error)
}
const unsubscribe = queryCache.subscribe(callback));
callback 함수는 쿼리 캐시가 업데이트 될 때마다 호출된다. 이 기능을 이용해 에러를 캐치해 처리하는 errorProvider를 만들어 전역으로 관리했다.
//memberApi.js
export const putMemberAuthChangeMutate = (userKey) => {
return useMutation((param) => putMemberAuthChangeFetch(param, userKey))
}
export const putMemberAuthChangeFetch = async (param, userKey) => {
return await request.put(PATH_ADMIN_API.MEMBER.AUTH_CHANGE(userKey),param).catch((error) => {
throw error
})
}
//member.js
const {mutate: changeAuth} = putMemberAuthChangeMutate(userKey);
const handleChangeAuth = () =>{
changeAuth(param,{
onSuccess: () => {
//성공 시 해당 queryKey의 api를 재실행
queryClient.invalidateQueries('memberLists')
queryClient.invalidateQueries('memberDetail')
}
})
}
참고