From 0a4cbb7481963cf7f4aa540f45be88f22c16b980 Mon Sep 17 00:00:00 2001 From: Merge Sim Date: Tue, 1 Sep 2026 13:41:55 -0700 Subject: [PATCH] fix(mobile): move native-chat latest-value ref writes out of render Render-time ref writes can leak from discarded render work (react-freeze makes discarded renders reachable on mobile) and fail the blocking React Doctor changed-lines CI gate. Use the layout-effect idiom already established in use-mobile-native-chat-active-resolution. Claude-Session: https://claude.ai/code/session_01CNFkBiVT51tM6UA5i2rcaK --- mobile/src/session/use-mobile-native-chat-image-upload.ts | 6 ++++-- mobile/src/session/use-mobile-structured-agent-state.ts | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/mobile/src/session/use-mobile-native-chat-image-upload.ts b/mobile/src/session/use-mobile-native-chat-image-upload.ts index 655bd5906d3..01567b5c727 100644 --- a/mobile/src/session/use-mobile-native-chat-image-upload.ts +++ b/mobile/src/session/use-mobile-native-chat-image-upload.ts @@ -1,4 +1,4 @@ -import { useCallback, useRef, useState } from 'react' +import { useCallback, useLayoutEffect, useRef, useState } from 'react' import { CLIPBOARD_IMAGE_TOO_LARGE_ERROR } from '../../../src/shared/clipboard-image' import type { RpcClient } from '../transport/rpc-client' import type { ConnectionState } from '../transport/types' @@ -46,7 +46,9 @@ export function useMobileNativeChatImageUpload(args: { const [isAttaching, setIsAttaching] = useState(false) const attachingCount = useRef(0) const connStateRef = useRef(connState) - connStateRef.current = connState + useLayoutEffect(() => { + connStateRef.current = connState + }, [connState]) const attachImage = useCallback( async (source: MobileImageSource): Promise => { diff --git a/mobile/src/session/use-mobile-structured-agent-state.ts b/mobile/src/session/use-mobile-structured-agent-state.ts index d29153b44f4..4c73f6f0c47 100644 --- a/mobile/src/session/use-mobile-structured-agent-state.ts +++ b/mobile/src/session/use-mobile-structured-agent-state.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react' import type { AgentSessionHistoryResult, AgentSessionSubscribeEvent @@ -37,7 +37,9 @@ export function useMobileStructuredAgentState(args: { const [state, setState] = useState(EMPTY_STRUCTURED_AGENT_SESSION) const [loadingOlder, setLoadingOlder] = useState(false) const stateRef = useRef(state) - stateRef.current = state + useLayoutEffect(() => { + stateRef.current = state + }, [state]) const apply = useCallback((action: StructuredAgentSessionAction) => { setState((current) => reduceStructuredAgentSession(current, action))