서버의 값을 클라이언트에 가져오거나, 캐싱, 값 업데이트, 에러핸들링 등 비동기 과정을 더욱 편하게 하는데 사용
클라이언트 데이터와 서버 데이터가 공존하는 문제
서버에는 이미 패치된 데이터가 클라이언트에서는 패치되기 전 데이터로 남아있을 수 있음
react-query는 서버와 클라이언트 데이터를 분리함
getNextPageParam
: lastPage를 인자로 전달 받아서 다음 페이지가 존재할 경우 다음페이지 값을fetchNextPage
함수의 인자로 전달
fetchNextPage = useInfiniteQuery의 두번째 콜백 함수 + getNextPageParam
staleTime
: 0refetchOnMount
refetchOnWindowFocus
refetchOnReconnect
: truecacheTime
: 60 * 5 * 1000retry
: 3QueryClient가 내부적으로 Context를 사용하기 때문
GET
function Todos() {
const { status, data, error } = useQuery("todos", fetchTodoList);
if (status === "loading") {
return <span>Loading...</span>;
}
if (status === "error") {
return <span>Error: {error.message}</span>;
}
return (
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
const { data: todoList, error, isFetching } = useQuery("todos", fetchTodoList);
const { data: nextTodo, error, isFetching } = useQuery(
"nextTodos",
fetchNextTodoList,
{
enabled: !!todoList // true가 되면 fetchNextTodoList를 실행한다
}
);
const usersQuery = useQuery("users", fetchUsers);
const teamsQuery = useQuery("teams", fetchTeams);
const projectsQuery = useQuery("projects", fetchProjects);
// 어짜피 세 함수 모두 비동기로 실행하는데, 세 변수를 개발자는 다 기억해야하고 세 변수에 대한 로딩, 성공, 실패처리를 모두 해야함
promise.all
처럼 useQuery를 실행
하나의 배열에 각 쿼리에 대한 상태 값이 객체로 반환됨
const result = useQueries([
{
queryKey: ["getRune", riot.version],
queryFn: () => api.getRunInfo(riot.version)
},
{
queryKey: ["getSpell", riot.version],
queryFn: () => api.getSpellInfo(riot.version)
}
]);
useEffect(() => {
console.log(result);
/* [
{rune 정보, data: [], isSucces: true ...},
{spell 정보, data: [], isSucces: true ...}
]*/
const loadingFinishAll = result.some(result => result.isLoading);
console.log(loadingFinishAll); // loadingFinishAll이 false이면 최종 완료
}, [result]);
unique key를 배열로 넣으면 query함수 내부에서 변수로 사용 가능
const { data: notice } = useQuery(
['fetchNotice', id],
() => fetchNotice(id),
{ enabled: !!token, staleTime: 60 * 60 * 1000 },
)
export const fetchNotice = async (id): Promise<string> => {
const {
data: { notice },
} = await api.get(`/notice/${id}`)
return notice
}
POST
const { mutate: postNoticeMutate } = useMutation(postNotice, {
mutationKey: 'postNotice',
onSuccess: () => {
// success!!
},
onError: (error: ErrorType) => {
// error!!
},
})
const onClickSubmit = async (notice) => {
await postNoticeMutate(notice)
}
export const postNotice = async (notice) => {
await api.post('/notice', { notice })
}
update후에 get 다시 실행
mutation 함수가 성공할 때, unique key로 맵핑된 get 함수를 invalidateQueries
const mutation = useMutation(postTodo, {
onSuccess: () => {
// postTodo가 성공하면 todos로 맵핑된 useQuery api 함수를 실행합니다.
queryClient.invalidateQueries("todos");
}
});
만약 mutation에서 return된 값을 이용해서 get 함수의 파라미터를 변경해야할 경우 setQueryData를 사용
const queryClient = useQueryClient();
const mutation = useMutation(editTodo, {
onSuccess: data => {
// data가 fetchTodoById로 들어간다
queryClient.setQueryData(["todo", { id: 5 }], data);
}
});
const { status, data, error } = useQuery(["todo", { id: 5 }], fetchTodoById);
mutation.mutate({
id: 5,
name: "nkh"
});
react query는 비동기를 조금 더? 선언적(선언형 프로그래밍 vs 명령형 프로그래밍, https://boxfoxs.tistory.com/430)으로 사용할 수 있음
을 더욱 직관적으로 할 수 있음
// src/index.js
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 0,
suspense: true
}
}
});
ReactDOM.render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</React.StrictMode>,
document.getElementById("root")
);
const { data } = useQurey("test", testApi, { suspense: true });
return (
// isLoading이 true이면 Suspense의 fallback 내부 컴포넌트가 보여집니다.
// isError가 true이면 ErrorBoundary의 fallback 내부 컴포넌트가 보여집니다.
<Suspense fallback={<div>loading</div>}>
<ErrorBoundary fallback={<div>에러 발생</div>}>
<div>{data}</div>
</ErrorBoundary>
</Supense>
);
https://kyounghwan01.github.io/blog/React/react-query/basic/
https://www.youtube.com/watch?v=MArE6Hy371c&t=6921s&ab_channel=%EC%9A%B0%EC%95%84%ED%95%9CTech