1- import { useEffect , useState } from 'react'
21import { useNavigate } from 'react-router-dom'
32
43import { useMutation , useQuery , useQueryClient } from '@tanstack/react-query'
54
65import { useAuthStore } from '@/features/auth/store/authStore'
76import { postLike , deleteLike , getLikeStatus } from '@/features/like/api/like'
7+ import type { LikeStatusResponse } from '@/features/like/type/like'
88
99export 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