From e17c6e6d043d536db667a8541eaddc7eb76096fc Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sat, 30 May 2026 02:36:59 -0700 Subject: [PATCH] perf: reuse mounted ref in notice cards (#3577) --- src/renderer/src/components/FirstLaunchBanner.tsx | 12 +++--------- src/renderer/src/components/StarNagCard.tsx | 12 +++--------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/renderer/src/components/FirstLaunchBanner.tsx b/src/renderer/src/components/FirstLaunchBanner.tsx index ed93d57168e..ba727f47282 100644 --- a/src/renderer/src/components/FirstLaunchBanner.tsx +++ b/src/renderer/src/components/FirstLaunchBanner.tsx @@ -33,11 +33,12 @@ // off), the notice never returns, because the cohort condition // (`optedIn === null`) clears in all three resolving paths. -import { useEffect, useRef, useState } from 'react' +import { useState } from 'react' import { X } from 'lucide-react' import { Button } from './ui/button' import { acknowledgeBanner, PRIVACY_URL, setOptIn as telemetrySetOptIn } from '../lib/telemetry' +import { useMountedRef } from '@/hooks/useMountedRef' type FirstLaunchBannerProps = { onResolve: () => void @@ -57,14 +58,7 @@ export function FirstLaunchBanner({ // wasted IPC round-trip, but the guard also blocks a Turn-off click // arriving mid-flight after an acknowledge (or vice versa). const [inFlight, setInFlight] = useState(false) - const mountedRef = useRef(true) - - useEffect(() => { - mountedRef.current = true - return () => { - mountedRef.current = false - } - }, []) + const mountedRef = useMountedRef() const handleAcknowledge = async (): Promise => { if (inFlight) { diff --git a/src/renderer/src/components/StarNagCard.tsx b/src/renderer/src/components/StarNagCard.tsx index 0b7f69a6034..385ad84fd64 100644 --- a/src/renderer/src/components/StarNagCard.tsx +++ b/src/renderer/src/components/StarNagCard.tsx @@ -1,8 +1,9 @@ -import { useEffect, useRef, useState } from 'react' +import { useEffect, useState } from 'react' import { Star, X } from 'lucide-react' import { Card } from './ui/card' import { Button } from './ui/button' import { useAppStore } from '../store' +import { useMountedRef } from '@/hooks/useMountedRef' /** * Persistent "star Orca on GitHub" notification card. @@ -19,7 +20,7 @@ export function StarNagCard(): React.JSX.Element | null { const [visible, setVisible] = useState(false) const [busy, setBusy] = useState(false) const [error, setError] = useState(false) - const mountedRef = useRef(true) + const mountedRef = useMountedRef() // Why: UpdateCard lives at the same bottom-right slot. When it is visible // (any non-idle / non-not-available state), stack the star-nag card above // it instead of overlapping — we must not cover a pending update prompt @@ -27,13 +28,6 @@ export function StarNagCard(): React.JSX.Element | null { const updateStatus = useAppStore((s) => s.updateStatus) const updateCardVisible = updateStatus.state !== 'idle' && updateStatus.state !== 'not-available' - useEffect(() => { - mountedRef.current = true - return () => { - mountedRef.current = false - } - }, []) - useEffect(() => { return window.api.starNag.onShow(() => { setError(false)