From 320499811ba052d074097e927e7cbea1ff0df96a Mon Sep 17 00:00:00 2001 From: Renn F Date: Fri, 12 Jun 2026 23:21:55 +0200 Subject: [PATCH] fix(panel): derive the retention input instead of syncing it in an effect The Transcript Retention card seeded its input from the settings query with a useEffect + setState, which trips react-hooks/set-state-in-effect (cascading renders). Derive the displayed value (edited ?? serverValue) during render instead; the user's edits live in 'edited', reset to null after a successful save so the field re-syncs to the server value. No effect, no setState-in-effect. --- .../settings/transcript-retention-card.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/panel/src/components/settings/transcript-retention-card.tsx b/panel/src/components/settings/transcript-retention-card.tsx index 052838bb..3e859423 100644 --- a/panel/src/components/settings/transcript-retention-card.tsx +++ b/panel/src/components/settings/transcript-retention-card.tsx @@ -1,6 +1,6 @@ "use client"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { settingsApi } from "@/lib/api"; import { @@ -21,24 +21,24 @@ const DEFAULT_RETENTION = "14"; export function TranscriptRetentionCard() { const queryClient = useQueryClient(); - const [days, setDays] = useState(DEFAULT_RETENTION); + // `edited` holds the user's in-progress input; null means "show the server + // value". Deriving the displayed value avoids syncing query state into local + // state with an effect (react-hooks/set-state-in-effect). + const [edited, setEdited] = useState(null); const { data: settings, isLoading } = useQuery({ queryKey: ["settings"], queryFn: settingsApi.getAll, }); - useEffect(() => { - const stored = settings?.[RETENTION_KEY]; - if (stored !== undefined) { - setDays(stored); - } - }, [settings]); + const serverValue = settings?.[RETENTION_KEY] ?? DEFAULT_RETENTION; + const days = edited ?? serverValue; const saveMutation = useMutation({ mutationFn: (value: string) => settingsApi.update(RETENTION_KEY, value), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["settings"] }); + setEdited(null); // re-sync the input to the freshly-saved server value toast.success("Transcript retention updated"); }, onError: (error) => { @@ -79,7 +79,7 @@ export function TranscriptRetentionCard() { min={1} value={days} disabled={isLoading} - onChange={(e) => setDays(e.target.value)} + onChange={(e) => setEdited(e.target.value)} className="max-w-[160px]" />