33
44"use client" ;
55
6- import React , { useState } from "react" ;
6+ import React , { useEffect , useRef , useState } from "react" ;
77
88type CopyState = "idle" | "copied" | "error" ;
99
1010export function CopyButton ( { text } : { text : string } ) {
1111 const [ state , setState ] = useState < CopyState > ( "idle" ) ;
12+ // Track the pending reset timer so we can clear it on unmount (avoids the
13+ // React 18 "state update on an unmounted component" warning) and also
14+ // overwrite a stale timer when the user clicks again before it fires.
15+ const resetTimerRef = useRef < ReturnType < typeof setTimeout > | null > ( null ) ;
16+
17+ useEffect (
18+ ( ) => ( ) => {
19+ if ( resetTimerRef . current ) clearTimeout ( resetTimerRef . current ) ;
20+ } ,
21+ [ ] ,
22+ ) ;
1223
1324 const copied = state === "copied" ;
1425 const error = state === "error" ;
@@ -23,17 +34,23 @@ export function CopyButton({ text }: { text: string }) {
2334 type = "button"
2435 onClick = { async ( e ) => {
2536 e . preventDefault ( ) ;
37+ // Clear any in-flight reset from a previous click so rapid-fire copies
38+ // don't race each other and flip the label back early.
39+ if ( resetTimerRef . current ) {
40+ clearTimeout ( resetTimerRef . current ) ;
41+ resetTimerRef . current = null ;
42+ }
2643 try {
2744 await navigator . clipboard . writeText ( text ) ;
2845 setState ( "copied" ) ;
29- setTimeout ( ( ) => setState ( "idle" ) , 1500 ) ;
46+ resetTimerRef . current = setTimeout ( ( ) => setState ( "idle" ) , 1500 ) ;
3047 } catch ( err ) {
3148 // Clipboard blocked (permissions, insecure context, etc.) — surface
3249 // the failure to the user so they know the button didn't work, and
3350 // log enough for devs to debug rather than silently swallowing.
3451 console . warn ( "[copy-button] clipboard write failed" , err ) ;
3552 setState ( "error" ) ;
36- setTimeout ( ( ) => setState ( "idle" ) , 2000 ) ;
53+ resetTimerRef . current = setTimeout ( ( ) => setState ( "idle" ) , 2000 ) ;
3754 }
3855 } }
3956 aria-label = { label }
0 commit comments