fix(hooks): useSessionState reset honours the current default, not a frozen one

Also found uncommitted in the shared tree; unrelated to agent panels, so it lands
on its own.

`reset` closed over `initialValue` from the first render, and its `useCallback` dep
list deliberately omitted it — with an eslint-disable to silence the warning that was
correctly pointing at the bug. Any caller whose default is computed (derived from
props, from a fetch, from another piece of state) got reset to whatever that default
happened to be on mount, which after the first render is the wrong value.

Reads through a ref instead, so reset always sees the current default. The
eslint-disable goes away because there is nothing left to suppress — the dep list is
honest now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 21:47:56 +00:00
co-authored by Claude Opus 5
parent 8773da5953
commit 7ce9289921
+5 -3
View File
@@ -76,6 +76,8 @@ export function useSessionState<T>(key: string, initialValue: T) {
valueRef.current = value; valueRef.current = value;
const setGlobalRef = useRef(setGlobal); const setGlobalRef = useRef(setGlobal);
setGlobalRef.current = setGlobal; setGlobalRef.current = setGlobal;
const initialValueRef = useRef(initialValue);
initialValueRef.current = initialValue;
const setValue = useCallback( const setValue = useCallback(
(arg: React.SetStateAction<T>) => { (arg: React.SetStateAction<T>) => {
@@ -86,11 +88,11 @@ export function useSessionState<T>(key: string, initialValue: T) {
[key], [key],
); );
// Read through the ref, not the closure: resetting means "whatever the default is now", and a
// `useCallback` over `initialValue` would freeze the default from the render that built the callback.
const reset = useCallback(() => { const reset = useCallback(() => {
clearSessionValue(key); clearSessionValue(key);
setGlobalRef.current(initialValue); setGlobalRef.current(initialValueRef.current);
// `initialValue` is read at reset time on purpose: resetting means "whatever the default is now".
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [key]); }, [key]);
return [value, setValue, reset] as const; return [value, setValue, reset] as const;