
๋๊ด์ ์ ๋ฐ์ดํธ๋ ์๋ฒ ์๋ต์ ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ,
์์ฒญ์ด ์ฑ๊ณตํ ๊ฑฐ๋ผ๊ณ ๊ฐ์ ํ๊ณ UI๋ฅผ ๋จผ์ ์ ๋ฐ์ดํธํ๋ ๋ฐฉ์์ด๋ค.
์ฆ, ์ฌ์ฉ์๊ฐ ์ด๋ค ์ก์ ์ ํ์ ๋, ๋ฐ๋ก ํ๋ฉด์ด ๋ฐ๋๊ณ ์ดํ ์๋ฒ์ ์์ฒญ์ ๋ณด๋ธ๋ค. ์ฑ๊ณตํ๋ฉด ๊ทธ๋๋ก ์ ์งํ๊ณ , ์คํจํ๋ฉด ๋กค๋ฐฑํ๋ ์ ๋ต์ด๋ค.
export const useBook = (bookId: string | undefined) => {
const [book, setBook] = useState<BookDetail | null>(null);
const { isloggedIn } = useAuthStore();
const showAlert = useAlert();
const likeToggle = () => {
if (!isloggedIn) {
showAlert('๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.');
return;
}
if (!book) return;
if (book.liked) {
// ๐ ๋๊ด์ ์ผ๋ก ์ํ ์
๋ฐ์ดํธ
setBook({
...book,
liked: false,
likes: book.likes - 1,
});
// ๐ก ์๋ฒ ์์ฒญ
unLikeBook(book.id).catch(() => {
// โ ์คํจ ์ ๋กค๋ฐฑ ์ฒ๋ฆฌ ํ์ (ํ์ฌ๋ ์๋ต๋์ด ์์)
});
} else {
setBook({
...book,
liked: true,
likes: book.likes + 1,
});
likeBook(book.id).catch(() => {
// โ ์คํจ ์ ๋กค๋ฐฑ ์ฒ๋ฆฌ ํ์
});
}
};
useEffect(() => {
if (!bookId) return;
fetchBook(bookId).then(setBook);
}, [bookId]);
return { book, likeToggle };
};
---
## ๐ ๏ธ Tip: ์บ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํจ๊ป ์ฐ๊ธฐ
`React Query`๋ `SWR` ๊ฐ์ ์บ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ ๊ฐํธํ๊ฒ ๊ตฌํํ ์ ์๋ค.
### ์: React Query์์์ ๋๊ด์ ์
๋ฐ์ดํธ
```tsx
const queryClient = useQueryClient();
const mutation = useMutation(updateTodo, {
// 1. ๋๊ด์ ์
๋ฐ์ดํธ
onMutate: async (newTodo) => {
await queryClient.cancelQueries(['todo', newTodo.id]);
const previousTodo = queryClient.getQueryData(['todo', newTodo.id]);
queryClient.setQueryData(['todo', newTodo.id], newTodo);
return { previousTodo };
},
// 2. ์คํจ ์ ๋กค๋ฐฑ
onError: (err, newTodo, context) => {
if (context?.previousTodo) {
queryClient.setQueryData(['todo', newTodo.id], context.previousTodo);
}
},
// 3. ์ฑ๊ณต ๋๋ ์คํจ ํ refetch
onSettled: (newTodo) => {
queryClient.invalidateQueries(['todo', newTodo.id]);
},
});