가끔 invalidateQueries가 먹히지 않을 때가 있었다.
먹히지 않으니 변경사항이 보여지는 게 아닌, 변경되기 전 사항을 보여주게 된다.
invalidate 메서드가 호출되면, React Query는 다음과 같은 작업을 한다고 한다.
캐시된 데이터를 유효하지 않게 표시하고
만료된 데이터가 존재하는 경우, 이전 데이터를 반환할 수 있도록 stale 상태로 변경하며,
백그라운드에서 캐시를 다시 가져오는 비동기 작업을 시작한다.
따라서 invalidate 메서드가 호출되었을 때 즉시 데이터가 갱신되지 않을 수 있다.
대신, React Query가 다음 요청을 할 때 데이터가 새로 고쳐질 것이라고 한다.
그래서 사용자에게 변경될 데이터를 미리 보여주는 낙관적 업데이트(Optimistic Update)를 사용해보려고 한다.
import { dbService } from '@/firebase';
import { doc, getDoc, increment, updateDoc } from 'firebase/firestore';
import React from 'react';
import { useMutation, useQuery, useQueryClient } from 'react-query';
function Index() {
const queryClient = useQueryClient();
// 파이어베이스 테스트 데이터
const getTest = async () => {
const data = await getDoc(doc(dbService, 'test', 'number'));
return data.data();
};
// 1 더하기
const plusNumber = async () => {
await updateDoc(doc(dbService, `test/number`), { count: increment(1) });
};
// 테스트 데이터 가져오기
const { data } = useQuery('testData', getTest);
const { mutate } = useMutation(plusNumber, {
onMutate: async () => {
await queryClient.cancelQueries('testData');
const oldItemData = queryClient.getQueryData('testData');
queryClient.setQueryData('testData', (old: any) => ({
// count: old.count + 1,
count: 2, // 사용자가 보게 될 데이터
}));
return { oldItemData };
},
onError: (err, values, context) => {
queryClient.setQueryData('testData', context?.oldItemData);
},
onSettled: async () => {
await queryClient.invalidateQueries('testData');
},
});
const onClickPlus = () => {
mutate();
};
return (
<div>
<div>{data?.count}</div>
<div>
<button onClick={onClickPlus}>더하기</button>
</div>
</div>
);
}
export default Index;
위 코드는 더하기 버튼을 눌렀을 때, 숫자를 +1시킨다.
하지만 데이터베이스에 +1이 되고, 더한 숫자를 가져와서 브라우저에 보여지기 전까지 사용자가 보는 숫자는 2다.
그래서 느리게 보면, 숫자 2가 먼저 보이고 나중에 +1이 된 숫자를 볼 수 있다.
사용자가 볼 데이터의 캐시 데이터를 조작해서 먼저 보여주고, 뒤에서 업데이트 된 데이터를 가져오는 방식이라고 한다.