-
Notifications
You must be signed in to change notification settings - Fork 28
fix: clear CopyButton timer on re-click and unmount #532
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| "use client"; | ||
|
|
||
| import { useState, useCallback } from "react"; | ||
| import { useState, useCallback, useRef, useEffect } from "react"; | ||
| import { Copy, Check } from "lucide-react"; | ||
| import { cn } from "@/lib/utils"; | ||
|
|
||
|
|
@@ -25,6 +25,15 @@ interface CopyButtonProps { | |
|
|
||
| export function CopyButton({ text, className }: CopyButtonProps) { | ||
| const [copied, setCopied] = useState(false); | ||
| const timeoutRef = useRef<ReturnType<typeof setTimeout>>(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 CRITICAL: React 19 Error: With Fix: const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);Note: Since this ref is used with |
||
|
|
||
| useEffect(() => () => clearTimeout(timeoutRef.current), []); | ||
|
|
||
| const showCopied = useCallback(() => { | ||
| clearTimeout(timeoutRef.current); | ||
| setCopied(true); | ||
| timeoutRef.current = setTimeout(() => setCopied(false), 2000); | ||
| }, []); | ||
|
|
||
| const handleCopy = useCallback(async () => { | ||
| try { | ||
|
|
@@ -33,24 +42,21 @@ export function CopyButton({ text, className }: CopyButtonProps) { | |
| } else if (!fallbackCopyText(text)) { | ||
| throw new Error("Both clipboard methods failed"); | ||
| } | ||
| setCopied(true); | ||
| setTimeout(() => setCopied(false), 2000); | ||
| showCopied(); | ||
| } catch (err) { | ||
| console.error("Failed to copy to clipboard:", err); | ||
| // Try fallback if the modern API threw | ||
| try { | ||
| if (fallbackCopyText(text)) { | ||
| setCopied(true); | ||
| setTimeout(() => setCopied(false), 2000); | ||
| showCopied(); | ||
| } | ||
| } catch { | ||
| // Both methods failed — do nothing | ||
| } | ||
| } | ||
| }, [text]); | ||
| }, [text, showCopied]); | ||
|
|
||
| return ( | ||
| <button | ||
| <button type="button" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Suggestion: Formatting consistency The <button type="button"
onClick={handleCopy}Consider moving it to its own line for consistency with the other attributes: <button
type="button"
onClick={handleCopy}Non-blocking — purely stylistic. |
||
| onClick={handleCopy} | ||
| title="Copy to clipboard" | ||
| className={cn( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: FailproofAI/failproofai
Length of output: 185
🏁 Script executed:
Repository: FailproofAI/failproofai
Length of output: 401
🏁 Script executed:
Repository: FailproofAI/failproofai
Length of output: 2441
🌐 Web query:
@types/react 19 useRef no argument overload removed initial value required💡 Result:
In React 19 and the corresponding
@types/reactpackage, theuseRefhook has been updated to require an explicit argument, removing the overload that allowed callinguseRef()without any initial value [1][2][3]. Key details regarding this change: Reasoning: This change was made to simplify the type signature ofuseRef, aligning it with other hooks likeuseStateandcreateContext[1][4][3]. It removes ambiguity and ensures consistent behavior [1][5]. Migration: If you previously useduseRef()without arguments, you should now passundefinedas the initial value:useRef(undefined)[1][5][4]. Ref Object Behavior: - All refs returned byuseRefare now mutable by default [1][5][3]. - The distinction betweenMutableRefObjectandRefObjecthas been simplified;useRefnow consistently returns aRefObjectthat is mutable [1][5][6]. - Convenience overloads exist foruseRef(null)anduseRef(undefined)to ensure proper typing for thecurrentproperty [1][5][4]. Automated Fixes: The React team provides a codemod as part of thereact-19preset (or specificallyrefobject-defaults/useRef-required-initial) to automatically convert existinguseRef()calls touseRef(undefined)[1][5][4]. You can run this using tools such asnpx types-react-codemod@latest preset-19 ./path-to-your-react-ts-files[3].Citations:
useRefDefinitelyTyped/DefinitelyTyped#64920Pass an initial value to
useRef. React 19’s typings require one here, so useuseRef<ReturnType<typeof setTimeout> | undefined>(undefined).🤖 Prompt for AI Agents