Skip to content

Commit 5905768

Browse files
committed
refactor: 좋아요 optimistic update 적용 (#113)
1 parent 324f9f5 commit 5905768

File tree

1 file changed

+47
-17
lines changed

1 file changed

+47
-17
lines changed

src/features/like/model/useLike.ts

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { useEffect, useState } from 'react'
21
import { useNavigate } from 'react-router-dom'
32

43
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
54

65
import { useAuthStore } from '@/features/auth/store/authStore'
76
import { postLike, deleteLike, getLikeStatus } from '@/features/like/api/like'
7+
import type { LikeStatusResponse } from '@/features/like/type/like'
88

99
export const useLikeStatus = (playlistId: number, options?: { enabled?: boolean }) => {
1010
return useQuery({
@@ -20,25 +20,60 @@ const useLike = (playlistId: number) => {
2020
const { isLogin } = useAuthStore()
2121
const navigate = useNavigate()
2222

23-
const [isLiked, setIsLiked] = useState<boolean>(false)
2423
const { data: statusData, isLoading } = useLikeStatus(playlistId, { enabled: isLogin })
25-
26-
useEffect(() => {
27-
setIsLiked(statusData?.isLiked ?? false)
28-
}, [statusData])
24+
const isLiked = statusData?.isLiked ?? false
2925

3026
const likeMutation = useMutation({
3127
mutationFn: () => postLike(playlistId),
32-
onSuccess: () => {
33-
setIsLiked(true)
28+
29+
onMutate: async () => {
30+
await queryClient.cancelQueries({ queryKey: ['likeStatus', playlistId] })
31+
const previous = queryClient.getQueryData(['likeStatus', playlistId])
32+
33+
// 낙관적 업데이트
34+
queryClient.setQueryData<LikeStatusResponse>(['likeStatus', playlistId], (old) => ({
35+
...(old ?? { isLiked: false }),
36+
isLiked: true,
37+
}))
38+
39+
return { previous }
40+
},
41+
onError: (_err, _vars, context) => {
42+
// context는 onMutate의 return 값(previous)
43+
if (context?.previous) {
44+
// 실패 시 UI를 원래대로 돌림
45+
queryClient.setQueryData(['likeStatus', playlistId], context.previous)
46+
}
47+
},
48+
49+
// 성공 실패 관계 없이 무조건 실행
50+
onSettled: () => {
3451
queryClient.invalidateQueries({ queryKey: ['likeStatus', playlistId] })
3552
},
3653
})
3754

3855
const unlikeMutation = useMutation({
3956
mutationFn: () => deleteLike(playlistId),
40-
onSuccess: () => {
41-
setIsLiked(false)
57+
58+
onMutate: async () => {
59+
await queryClient.cancelQueries({ queryKey: ['likeStatus', playlistId] })
60+
const previous = queryClient.getQueryData(['likeStatus', playlistId])
61+
62+
queryClient.setQueryData<LikeStatusResponse>(['likeStatus', playlistId], (old) => ({
63+
...(old ?? { isLiked: false }),
64+
isLiked: true,
65+
}))
66+
67+
return { previous }
68+
},
69+
70+
onError: (_err, _vars, context) => {
71+
if (context?.previous) {
72+
queryClient.setQueryData(['likeStatus', playlistId], context.previous)
73+
}
74+
},
75+
76+
onSettled: () => {
4277
queryClient.invalidateQueries({ queryKey: ['likeStatus', playlistId] })
4378
},
4479
})
@@ -49,13 +84,8 @@ const useLike = (playlistId: number) => {
4984
return
5085
}
5186

52-
if (likeMutation.isPending || unlikeMutation.isPending) return
53-
54-
if (isLiked) {
55-
unlikeMutation.mutate()
56-
} else {
57-
likeMutation.mutate()
58-
}
87+
if (isLiked) unlikeMutation.mutate()
88+
else likeMutation.mutate()
5989
}
6090

6191
return { liked: isLiked, toggleLike, isLoading }

0 commit comments

Comments
 (0)