From 724f54c2c1f4a1efcd7423a5ff0e3db625629815 Mon Sep 17 00:00:00 2001 From: Qiaochu Hu <110hqc@gmail.com> Date: Fri, 13 Mar 2026 03:32:00 +0800 Subject: [PATCH] fix: clean up timeout in PlanSidebar to prevent memory leaks (#949) Co-authored-by: hobostay --- apps/web/src/components/PlanSidebar.tsx | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/PlanSidebar.tsx b/apps/web/src/components/PlanSidebar.tsx index c465f398907bdeb8beaed3b589e25ff3e54afc68..2d898b009e384d8a392d79d76320a373131d9822 100644 --- a/apps/web/src/components/PlanSidebar.tsx +++ b/apps/web/src/components/PlanSidebar.tsx @@ -1,4 +1,4 @@ -import { memo, useState, useCallback } from "react"; +import { memo, useState, useCallback, useRef, useEffect } from "react"; import { Badge } from "./ui/badge"; import { Button } from "./ui/button"; import { ScrollArea } from "./ui/scroll-area"; @@ -66,6 +66,7 @@ const PlanSidebar = memo(function PlanSidebar({ const [proposedPlanExpanded, setProposedPlanExpanded] = useState(false); const [isSavingToWorkspace, setIsSavingToWorkspace] = useState(false); const [copied, setCopied] = useState(false); + const copiedTimerRef = useRef | null>(null); const planMarkdown = activeProposedPlan?.planMarkdown ?? null; const displayedPlanMarkdown = planMarkdown ? stripDisplayedPlanMarkdown(planMarkdown) : null; @@ -74,8 +75,15 @@ const PlanSidebar = memo(function PlanSidebar({ const handleCopyPlan = useCallback(() => { if (!planMarkdown) return; void navigator.clipboard.writeText(planMarkdown); + if (copiedTimerRef.current != null) { + clearTimeout(copiedTimerRef.current); + } + setCopied(true); - setTimeout(() => setCopied(false), 2000); + copiedTimerRef.current = setTimeout(() => { + setCopied(false); + copiedTimerRef.current = null; + }, 2000); }, [planMarkdown]); const handleDownload = useCallback(() => { @@ -120,6 +128,14 @@ const PlanSidebar = memo(function PlanSidebar({ {/* Header */}
+ // Cleanup timeout on unmount + useEffect(() => { + return () => { + if (copiedTimerRef.current \!= null) { + clearTimeout(copiedTimerRef.current); + } + }; + }, []);