perf(site): move useSpeechRecognition into compiled path (#23689)

The hook lived at src/hooks/, outside the React Compiler scope.
It returned a new object literal every render, causing three
handler guards and two downstream JSX guards in AgentChatInput
(233 cache slots) to always miss.

Move the hook to src/pages/AgentsPage/hooks/ where the compiler
processes it. The compiler auto-memoizes the return object, so
manual useMemo is unnecessary.

Also replace ctorRef.current render-time access with a useState
lazy initializer. The ref access caused a CompileError that
would have prevented compilation. Browser API availability is
constant, so useState captures it once.
This commit is contained in:
Mathias Fredriksson
2026-03-26 22:34:34 +02:00
committed by GitHub
parent beece6d351
commit f3a8096ff6
3 changed files with 8 additions and 12 deletions
@@ -57,11 +57,11 @@ import {
TooltipContent,
TooltipTrigger,
} from "#/components/Tooltip/Tooltip";
import { useSpeechRecognition } from "#/hooks/useSpeechRecognition";
import { cn } from "#/utils/cn";
import { countInvisibleCharacters } from "#/utils/invisibleUnicode";
import { isMobileViewport } from "#/utils/mobile";
import { useOverflowCount } from "../hooks/useOverflowCount";
import { useSpeechRecognition } from "../hooks/useSpeechRecognition";
import {
fetchTextAttachmentContent,
formatTextAttachmentPreview,
@@ -85,17 +85,13 @@ export function useSpeechRecognition(): {
const [error, setError] = useState<string | null>(null);
const recognitionRef = useRef<SpeechRecognitionInstance | null>(null);
// Cache the constructor lookup once per hook instance so we don't hit
// the window property on every render.
const ctorRef = useRef<SpeechRecognitionConstructor | undefined>(
getSpeechRecognitionCtor(),
);
const isSupported = ctorRef.current !== undefined;
// Browser API availability is constant for the lifetime of the
// page, so a lazy state initializer captures it once.
const [ctorSnapshot] = useState(getSpeechRecognitionCtor);
const isSupported = ctorSnapshot !== undefined;
const start = useCallback(() => {
const Ctor = ctorRef.current;
if (!Ctor) {
if (!ctorSnapshot) {
return;
}
@@ -107,7 +103,7 @@ export function useSpeechRecognition(): {
setError(null);
const recognition = new Ctor();
const recognition = new ctorSnapshot();
recognition.lang = navigator.language;
recognition.continuous = true;
recognition.interimResults = true;
@@ -147,7 +143,7 @@ export function useSpeechRecognition(): {
setTranscript("");
setIsRecording(true);
recognition.start();
}, []);
}, [ctorSnapshot]);
const stop = useCallback(() => {
// stop() lets the browser deliver any remaining final results