@@ -9,6 +9,8 @@ export default function HeaderService({ recipient }) {
99 const [ reactions , setReactions ] = useState ( [ ] ) ;
1010 const [ showEmojiPicker , setShowEmojiPicker ] = useState ( false ) ;
1111 const [ showAllEmojisDropdown , setShowAllEmojisDropdown ] = useState ( false ) ;
12+ const pendingEmojisRef = useRef ( [ ] ) ;
13+ const [ pendingEmojis , setPendingEmojis ] = useState ( [ ] ) ;
1214 const emojiPickerRef = useRef ( null ) ;
1315 const emojiMoreRef = useRef ( null ) ;
1416 const [ isLargeScreen , setIsLargeScreen ] = useState ( true ) ;
@@ -81,16 +83,45 @@ export default function HeaderService({ recipient }) {
8183
8284 const handleAddReaction = async ( emoji ) => {
8385 if ( ! recipient ?. id ) return ;
86+ if ( pendingEmojisRef . current . includes ( emoji ) ) return ;
87+
88+ pendingEmojisRef . current = [ ...pendingEmojisRef . current , emoji ] ;
89+ setPendingEmojis ( pendingEmojisRef . current ) ;
90+
91+ setReactions ( ( prev ) => {
92+ const found = prev . find ( ( r ) => r . emoji === emoji ) ;
93+ if ( found ) {
94+ return prev . map ( ( r ) =>
95+ r . emoji === emoji ? { ...r , count : r . count + 1 } : r ,
96+ ) ;
97+ } else {
98+ return [ ...prev , { id : `optimistic-${ emoji } ` , emoji, count : 1 } ] ;
99+ }
100+ } ) ;
84101
85102 try {
86103 await addReaction ( recipient . id , emoji ) ;
87-
88104 const updatedReactions = await fetchReactions ( recipient . id ) ;
89105 setReactions ( updatedReactions ) ;
106+ } catch ( error ) {
107+ console . error ( error ) ;
108+ setReactions ( ( prev ) => {
109+ const found = prev . find ( ( r ) => r . emoji === emoji ) ;
110+ if ( found && found . count === 1 && found . id ?. startsWith ( 'optimistic-' ) ) {
111+ return prev . filter ( ( r ) => r . emoji !== emoji ) ;
112+ } else {
113+ return prev . map ( ( r ) =>
114+ r . emoji === emoji ? { ...r , count : r . count - 1 } : r ,
115+ ) ;
116+ }
117+ } ) ;
118+ } finally {
119+ pendingEmojisRef . current = pendingEmojisRef . current . filter (
120+ ( e ) => e !== emoji ,
121+ ) ;
122+ setPendingEmojis ( pendingEmojisRef . current ) ;
90123 setShowEmojiPicker ( false ) ;
91124 setShowAllEmojisDropdown ( false ) ;
92- } catch ( error ) {
93- console . error ( '이모지 추가 실패:' , error ) ;
94125 }
95126 } ;
96127
@@ -148,13 +179,19 @@ export default function HeaderService({ recipient }) {
148179 < div className = { styles [ 'header-service__emojis' ] } >
149180 { topReactions . length > 0 ? (
150181 topReactions . map ( ( reaction ) => (
151- < div
182+ < button
152183 key = { reaction . id }
153184 className = { styles [ 'header-service__emoji-item' ] }
154185 onClick = { ( ) => handleAddReaction ( reaction . emoji ) }
186+ disabled = { pendingEmojis . includes ( reaction . emoji ) }
155187 >
156- { reaction . emoji } { reaction . count }
157- </ div >
188+ < span className = { styles [ 'header-service__emoji' ] } >
189+ { reaction . emoji }
190+ </ span > { ' ' }
191+ < span className = { styles [ 'header-service__count' ] } >
192+ { reaction . count }
193+ </ span >
194+ </ button >
158195 ) )
159196 ) : (
160197 < span > 아직 리액션이 없어요</ span >
@@ -188,15 +225,21 @@ export default function HeaderService({ recipient }) {
188225 . sort ( ( a , b ) => b . count - a . count )
189226 . slice ( 0 , isLargeScreen ? 8 : 6 )
190227 . map ( ( reaction ) => (
191- < div
228+ < button
192229 key = { reaction . id }
193230 className = {
194231 styles [ 'header-service__emoji-dropdown-item' ]
195232 }
196233 onClick = { ( ) => handleAddReaction ( reaction . emoji ) }
234+ disabled = { pendingEmojis . includes ( reaction . emoji ) }
197235 >
198- { reaction . emoji } { reaction . count }
199- </ div >
236+ < span className = { styles [ 'header-service__emoji' ] } >
237+ { reaction . emoji }
238+ </ span > { ' ' }
239+ < span className = { styles [ 'header-service__count' ] } >
240+ { reaction . count }
241+ </ span >
242+ </ button >
200243 ) )
201244 ) : (
202245 < span > 아직 리액션이 없어요</ span >
0 commit comments