const fetchData = async () => {
try {
const response = await todoApi.get("/todos?_sort=-createdAt");
setData(response.data);
} catch (err) {
setError(err);
} finally {
setIsLoading(false);
}
};
try vs finally중에 뭐가 먼저 실행될까?
=> 둘다!
tanstack query
- isPending
- isLoading -> inPending 으로 명칭 변경
- QueryClient에 아직 데이터가 없고 쿼리가 아직 실행 대기중인지 여부
- 쿼리가 현재 실행중인지 여부 (초기로딩 및 refetching 을 모두 포함)
- 얕은비교를 해서 재렌더링 방지
- useQuery 에서 언제 상태변경요청(setState) 하는가?
- → queryFn이 실행되어 값을 반환하는 즉시, 그 값이 QueryClient 의 서버상태로 설정됩니다.
- isPending vs isFetching
- isPending은 QueryClient에 아직 데이터가 없고 쿼리가 아직 실행 대기중인지 여부
- isFetching은 쿼리가 현재 실행중인지 여부 (초기로딩 및 refetching 을 모두 포함)
- QueryFunctionContext
- queryFn 의 매개변수로 전달되는 객체
const { data, isPending, error } = useQuery({
queryKey: ["todos", id],
queryFn: async (context) => {
const { queryKey, meta, signal, pageParam } = context; // QueryFunctionContext
const [,id] = queryKey;
const response = await todoApi(`/todos/${id}`);
return response.data;
},
});
- QueryCache 를 통한 Global Callback 을 통한 에러처리 (toast같은 alert 라이브러리 사용가능)
- 첫번째 매개변수 : error (에러)
- 두번째 매개변수 : query (어디서온 에러인지)
- query cahe란 밑에 사진
// main.jsx
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: (error, query) => {
if(query.meta.source === "todos") {
toast.error(`Something went wrong in TodoList: ${error.message}`),
}
}
}),
});
- enabled 옵션
- 마치 수도꼭지같이 실행 조정
- 기본값은 true
- true인 경우에만 queryFn 실행
- Disabling/Pausing Queries (이벤트 발생 시에만 수동 실행하고 싶을 때)
- Dependent Queries (useQuery 2개 이상이며 실행순서 설정 필요할 때)
- select 옵션
- queryFn 에 의해 리턴된 값을 변형시킨 후에 useQuery 의 리턴 data로 넘겨줌.
- (단, cache data 는 queryFn 에서 리턴받은 값 그대로임)
- 데이터 변경은 queryFn에서!
import { useQuery } from 'tanstack/react-query'
function User() {
const { data } = useQuery({
queryKey: ['user'],
queryFn: fetchUser,
select: (user) => user.username,
})
return <div>Username: {data}</div>
}
- 낙관적 업데이트(Optimistic Update)
- 서버 요청이 정상적으로 잘 될거란 가정하에 UI 변경을 먼저하고, 서버 요청 하는 방식
- 혹시라도 서버 요청이 실패하는 경우, UI 를 원상복구(revert / roll back)
const queryClient = useQueryClient()
useMutation({
mutationFn: updateTodo, // 실행 2
// When mutate is called:
onMutate: async (newTodo) => { // 실행 1
// Cancel any outgoing refetches
// (so they don't overwrite our optimistic update)
await queryClient.cancelQueries({ queryKey: ['todos'] })
// Snapshot the previous value
const previousTodos = queryClient.getQueryData(['todos'])
// Optimistically update to the new value
queryClient.setQueryData(['todos'], (old) => [...old, newTodo])
// Return a context object with the snapshotted value
return { previousTodos }
},
// If the mutation fails,
// use the context returned from onMutate to roll back
onError: (err, newTodo, context) => {
queryClient.setQueryData(['todos'], context.previousTodos)
},
onSettled: () => { //성공하든 실패하든 항상 실행
queryClient.invalidateQueries({ queryKey: ['todos'] })
},
})
- useMutation 의 mutationFn 의 매개변수를 한 개만 받음
const { mutate: handleLike } = useMutation({
// 💩 mutationFn의 매개변수는 한 개만 받을 수 있어요!
mutationFn: (id, currentLiked) =>
todoApi.patch(`/todos/${id}`, { liked: !currentLiked }),
onSuccess: () => {
queryClient.invalidateQueries(["todos"]);
}
})
const { mutate: handleLike } = useMutation({
// 🎉 객체 구조분해할당을 이용해서 여러개의 데이터를 매개변수로 받을 수 있어요!
mutationFn: ({ id, currentLiked }) =>
todoApi.patch(`/todos/${id}`, { liked: !currentLiked }),
onSuccess: () => {
queryClient.invalidateQueries(["todos"]);
}
})
'개발 일기' 카테고리의 다른 글
2024-12-06(optimistic update) (0) | 2024.12.06 |
---|---|
2024-12-02(tanstack query - 더보기, 무한스크롤) (1) | 2024.12.02 |
2024-11-27(상태관리,zustand) (0) | 2024.11.27 |
2024-11-25(rest api , axios, tanstack query) (0) | 2024.11.25 |
2024-11-22(비동기,http상태) (0) | 2024.11.22 |