React Query를 사용하기전에 Redux, Redux Toolkit, Redux Saga를 이용했다.
function getPostAPI(lastItemId) {
return axios.get(`${BASE_URL}/posts`, {
params: { take: 5, lastItemId },
});
}
function* getPostSaga() {
try {
const lastItemId = yield select(({ post }) => post.posts.lastItemId);
const {
data: { posts, lastId },
} = yield call(getPostAPI, lastItemId);
yield put(getPostSuccess({ posts, lastId, targetField: "posts" }));
} catch (error) {
yield put(getPostError);
console.log(error);
alert(error.response.data.message);
}
}
//이런식의 패턴이 반복됨
PostSlice.ts
const postSlice = createSlice({
name: "post",
initialState: {
loading: false,
createCommentLoading: null,
patchPostLoading: null,
createPostLoading: false,
posts: { data: [], hasMore: true, lastItemId: null },
myPosts: { data: [], hasMore: true, lastItemId: null },
modalVisibleValue: false,
},
reducers: {
getPostRequest(state) {
state.loading = true;
},
getPostSuccess(state, { payload }) {
state.loading = false;
const target = state[payload.targetField];
if (!target.lastItemId || state.modalVisibleValue) {
target.data = payload.posts;
} else {
target.data = [...target.data, ...payload.posts];
}
target.lastItemId = payload.posts[payload.posts.length - 1].id;
target.hasMore = target.lastItemId !== payload.lastId;
},
getPostError(state) {
state.loading = false;
},
//이런식의 패턴 반복
이런식의 패턴이 계속 반복되면서 서버와 통신하는데, 나중에 보면 store에 클라이언트 데이터와 서버 데이터가 공존된다. 또한 코드를 쭉 읽어보면 이 코드들이 상태관리를 위한 코드인지 아니면 API통신을 위한 코드인지 의문이 든다.
그리고
initialState: {
loading: false,
createCommentLoading: null,
patchPostLoading: null,
createPostLoading: false,
posts: { data: [], hasMore: true, lastItemId: null },
myPosts: { data: [], hasMore: true, lastItemId: null },
modalVisibleValue: false,
},
에서 서버에 요청을 보내면 loading이 true... 요청이 끝나면 false로 바뀌는 형태가 계속 반복이 된다.
또한 posts,myPost안에 data안에 서버로부터 온 게시물 data들이 저장되어 있다. 또한 새로운 글을 작성하거나 새로운 댓글을 달면 서버로 data를 post를 하고 또 내 store에 값을 직접 넣어줘야한다(새로고침 안하고 새로운 게시물 or 댓글을 바로 보일 수 있게 하기 위해서)
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { RecoilRoot } from "recoil";
import { QueryClientProvider, QueryClient } from "react-query";
const root = ReactDOM.createRoot(document.getElementById("root") as Element);
const queryClient = new QueryClient();
root.render(
<React.StrictMode>
<RecoilRoot>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</RecoilRoot>
</React.StrictMode>
);
우선 Redux를 사용할때
<Provider store={store}>
<App />
</Provider>
이런식으로 Provider을 사용해서 App를 감싸는것처럼
QueryClientProvider를 사용해서 App를 감싼다.
const {
data,
dataUpdatedAt,
error,
errorUpdatedAt,
failureCount,
isError,
isFetched,
isFetchedAfterMount,
isFetching,
isIdle,
isLoading,
isLoadingError,
isPlaceholderData,
isPreviousData,
isRefetchError,
isRefetching,
isStale,
isSuccess,
refetch,
remove,
status,
} = useQuery(queryKey, queryFn?, {
cacheTime,
enabled,
initialData,
initialDataUpdatedAt
isDataEqual,
keepPreviousData,
meta,
notifyOnChangeProps,
notifyOnChangePropsExclusions,
onError,
onSettled,
onSuccess,
placeholderData,
queryKeyHashFn,
refetchInterval,
refetchIntervalInBackground,
refetchOnMount,
refetchOnReconnect,
refetchOnWindowFocus,
retry,
retryOnMount,
retryDelay,
select,
staleTime,
structuralSharing,
suspense,
useErrorBoundary,
})
// or using the object syntax
const result = useQuery({
queryKey,
queryFn,
enabled,
})
공식 문서를 보면 뭐가 엄청 길다...
서버에 get를 요청할때 사용한다.
간단하게 Options부터 살펴보면
useQuery(queryKey, queryFn?, {....})
queryKey : 첫번째 파라미터이고
string | unknown[]
를 받는다.
꼭 들어가야하는 필수 파라미터이며 useQuery에 부여되는 고유 Key값이다.
queryFn: 두번째 파라미터이고
(context: QueryFunctionContext) => Promise<TData>
필수 파라미터이며 쿼리가 데이터를 요청하는 데 사용할 함수이다.
세번째 파라미터에는 객체가 오는데 필수가 아니고 옵션이다.
몇개만 소개하자면
enabled:(boolean)
기본값은 true이고 false로 바꾸면 query가 비활성화된다.
retry:(boolean | number | (failureCount: number, error: TError) => boolean)
false인 경우 실패한 쿼리는 재시도하지 않는다.
true인 경우 실패한 쿼리는 무한히 재시도한다.
숫자로 설정된 경우에는 실패한 쿼리 수가 해당 숫자를 충족할 때까지 실패한 쿼리를 다시 시도한다.
onSuccess:((data: TData) => void)
쿼리가 성공적으로 새 데이터를 가져오거나 setQueryData를 통해 캐시가 업데이트될 때마다 함수를 실행한다.
onError:((error: TError) => void)
쿼리에 에러가 발생하고 에러가 전달되면 이 함수가 실행된다.
이제는 return값을 살펴보자
몇개만 소개하자면
data:
기본값은 undefined이고 쿼리가 성공적일때 받는 데이터이다.
error:
기본값은 null이고 에러가 발생했을때 쿼리가 던져주는 error객체이다
status:
idle - query가 쉬고 있는 상태, enabled: false일때와 초기 상태일때
loading - fetching중인 상태
error- fetch에 실패한 상태
success- fetch 성공한 상태
const {
data,
error,
isError,
isIdle,
isLoading,
isPaused,
isSuccess,
mutate,
mutateAsync,
reset,
status,
} = useMutation(mutationFn, {
mutationKey,
onError,
onMutate,
onSettled,
onSuccess,
retry,
retryDelay,
useErrorBoundary,
meta,
})
mutate(variables, {
onError,
onSettled,
onSuccess,
})
useMutation는 useQuery와 다르게 데이터를 create, update, delete 할때 사용한다
아까 useQuery보다는 짧다...
Options부터 살펴보자
useMutation(mutationFn, {....})
mutationFn:
첫번째 파라미터이고 필수로 있어야한다.
onSettled:
필수가 아닌 옵션이며, mutation의 성공했을때의 데이터 또는 실패했을때 error를 받아서 실행시킨다
onSuccess:
필수가 아닌 옵션이며, mutation이 성공 했을때 data를 파라미터로 받고 실생시킨다
onError:
필수가 아닌 옵션이며, mutation이 실패 했을때 error를 파라미터로 받고 실생시킨다
return 값에는
data:
성공했을때 나오는 data값이며, 기본값은 undifined이다
mutate:
안에 보내고 싶은 variables들을 넣어서 mutation을 실행시킬 수 있다. variables들은 mutationFn에 전달되는 객체이다.
useQuery와 useMutation뿐만 아니라 useInfiniteQuery도 있는데 무한 스크롤을 구현할때 유용하게 쓰인다. 좀 더 공부를 해봐야겠다.