제가 react query를 사용하게 된 이유는 클라이언트 상태 관리로는 서버 상태관리가 쉽지 않았고, 그것을 해결하기 위해 리덕스로 비동기 처리를 하기에는 너무나도 많은 작업이 필요했기에 이를 간편화 할 방법을 찾다가 react query를 사용하게 되었습니다.
뿐만아니라 react query를 이용함으로써 서스펜스와 에러바운더리를 통해 로딩과 에러를 간단하게 할 수 있었으며, 캐싱이 가능하여 데이터 관리가 편했습니다.
npm install react-query
or
yarn add react-query
프로젝트를 생성한 후 react query를 install 해줍니다.
javascript
import "../styles/globals.css";
import type { AppProps } from "next/app";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
function MyApp({ Component, pageProps }: AppProps) {
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
<Component {...pageProps} />
</QueryClientProvider>
);
}
export default MyApp;
루트 페이지(app.tsx)에 react-query를 세팅합니다.
저는 nextjs로 예제코드를 작성하여 _app.tsx
에 세팅했습니다.
export default function reactQueryTest(props: Props) {
const { data, isLoading, isError, error } = useQuery<FetcherApi, AxiosError>(
["test-data"],
() => fetcherApi("get", "/users?page=2"), //인자값을 넣을수도 있고, axios 라이브러리로도 가능
{
//성공 (then)
onSuccess: (resposne) => {
console.log("onSuccess");
console.log(resposne);
},
// 실패 (catch)
// 네트워크 에러(401,404 등)는 호출이 되지 않고, 서버 api 호출이 실패한 경우만 호출됩니다.
onError: (error) => {
console.log("onError");
console.log(error?.response?.status);
},
//성공 실패 상관없이 실행 (finally)
onSettled: () => {
console.log("onSettled");
},
}
);
console.log(data);
if (isLoading) {
return <div>로딩중...</div>;
}
if (isError) {
return <h1>{error.message}</h1>;
}
return (
<div>
<h1>{data.page}</h1>
<h1>{data.per_page}</h1>
<h1>{data.total}</h1>
<h1>{data.total_pages}</h1>
</div>
);
}
const { data, isLoading, isError, error } = useQuery<Post, AxiosError>(
["test-data"],
() => post("get", "/users?page=2"),
{
onSuccess:(data: TData) => void,
onError:(error: TError) => void,
onSettled:(data?: TData, error?: TError) => void,
staleTime: number | Infinity,
cacheTime: number | Infinity,
enabled: boolean,
suspense: boolean,
retry: boolean | number | (failureCount: number, error: TError) => boolean
select: (data: TData) => unknown,
keepPreviousData: boolean,
refetchInterval: number | false | ((data: TData | undefined, query: Query) => number | false)
}
);
프로젝트가 커지면 useQuery를 여러개 사용해야 할 경우가 발생하는데, 그럴 땐 useQueries를 사용 가능합니다.
예제
useQuery 사용
const {
data: animalData,
isLoading: animalLoading,
isError: animalError,
} = useQuery<FetcherApi, AxiosError>(["animal"], () =>
fetcherAnimal("/users?page=1")
);
const {
data: insectData,
isLoading: insectLoading,
isError: insectError,
} = useQuery<FetcherApi, AxiosError>(["insect"], () =>
fetcherInsect("/users?page=2")
);
const {
data: personData,
isLoading: personLoading,
isError: personError,
} = useQuery<FetcherApi, AxiosError>(["person"], () =>
fetcherPerson("/users?page=3")
);
useQueries 사용
const result = useQueries<FetcherApi[]>([
{
queryKey: ["animal"],
queryFn: () => fetcherAnimal("/users?page=1"),
},
{
queryKey: ["insect"],
queryFn: () => fetcherInsect("/users?page=2"),
},
{
queryKey: ["person"],
queryFn: () => fetcherPerson("/users?page=3"),
},
]);
import { useMutation } from "@tanstack/react-query";
import React, { FormEvent, useState } from "react";
import { fetcherPostApi } from "../api";
type Props = {};
export default function reactQueryMuateTest(props: Props) {
const [name, setName] = useState<string>("");
const [job, setJob] = useState<string>("");
const { mutate, isLoading } = useMutation(fetcherPostApi, {
onMutate(variables) {
console.log("onMutate -> ");
console.log(variables);
},
onSuccess: (data, variables, context) => {
console.log("success -> ", data, variables, context);
},
onError: (error, variable, context) => {
console.log("onError -> ", error, variable, context);
},
onSettled: () => {
console.log("onSettled");
},
});
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
mutate({
name: name,
job: job,
});
};
return (
<div>
<h1>React query useMutate 예제</h1>
{isLoading ? (
<div>로딩중...</div>
) : (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<input
type="text"
value={job}
onChange={(e) => setJob(e.target.value)}
/>
<button type="submit">제출하기</button>
</form>
)}
</div>
);
}
const { mutate, isLoading } = useMutation(fetcherPostApi, {
onMutate(variables) {
console.log("onMutate -> ");
console.log(variables);
},
onSuccess: (data, variables, context) => {
console.log("success -> ", data, variables, context);
// queryClient를 활용해 특정 key의 useQuery를 재호출이 가능합니다.
queryClient.invalidateQueries("person");
},
onError: (error, variable, context) => {
console.log("onError -> ", error, variable, context);
},
onSettled: () => {
console.log("onSettled");
},
});