From 9ea5f0488dcbe363836bb71ed7980560a9ce8da8 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Mon, 31 Aug 2026 22:14:35 -0700 Subject: [PATCH] fix(mobile): keep split session hooks render-pure Documents the pre-existing render-time refs the split relocated onto changed lines, and drops a ref assignment the split added that the monolith never had. --- mobile/src/session/use-mobile-session-accessory-selection.ts | 1 + mobile/src/session/use-mobile-session-feedback-capabilities.ts | 2 ++ mobile/src/session/use-mobile-session-startup.ts | 2 ++ mobile/src/session/use-mobile-session-terminal-runtime.ts | 1 - .../use-mobile-session-terminal-subscription-foundation.ts | 2 ++ 5 files changed, 7 insertions(+), 1 deletion(-) diff --git a/mobile/src/session/use-mobile-session-accessory-selection.ts b/mobile/src/session/use-mobile-session-accessory-selection.ts index 069212f4334..356bf11952d 100644 --- a/mobile/src/session/use-mobile-session-accessory-selection.ts +++ b/mobile/src/session/use-mobile-session-accessory-selection.ts @@ -46,6 +46,7 @@ export function useMobileSessionAccessorySelection(scope: MobileSessionTerminalI const repeatIntervalRef = useRef | null>(null) // Why: ref keeps repeat firing the current callback; else a mid-hold tab switch/reconnect routes bytes to a stale terminal. const handleAccessoryKeyRef = useRef(handleAccessoryKey) + // react-doctor-disable-next-line react-doctor/no-ref-current-in-render handleAccessoryKeyRef.current = handleAccessoryKey const stopAccessoryRepeat = useCallback(() => { if (repeatTimeoutRef.current) { diff --git a/mobile/src/session/use-mobile-session-feedback-capabilities.ts b/mobile/src/session/use-mobile-session-feedback-capabilities.ts index 66d2abf1bcd..8c619fb1f7f 100644 --- a/mobile/src/session/use-mobile-session-feedback-capabilities.ts +++ b/mobile/src/session/use-mobile-session-feedback-capabilities.ts @@ -35,6 +35,7 @@ export function useMobileSessionFeedbackCapabilities(scope: MobileSessionTermina // Why: stable callbacks (handleFileTap) read the live value via this ref, since // the capability probe resolves after the callbacks are created. const browserScreencastSupportedRef = useRef(browserScreencastSupported) + // react-doctor-disable-next-line react-doctor/no-ref-current-in-render browserScreencastSupportedRef.current = browserScreencastSupported // Why: terminal gesture/input callbacks are stable/imperative, so keep their refs current before commit, not in a later effect. clientRef.current = client @@ -49,6 +50,7 @@ export function useMobileSessionFeedbackCapabilities(scope: MobileSessionTermina ) // Why: Expo can reuse this screen for a new route; reconcile before paint so a dismissed old warning doesn't flash. if (reconciledCreateWarningState !== createWarningState) { + // react-doctor-disable-next-line react-doctor/no-prop-callback-in-render setCreateWarningState(reconciledCreateWarningState) } const createWarning = reconciledCreateWarningState.visible diff --git a/mobile/src/session/use-mobile-session-startup.ts b/mobile/src/session/use-mobile-session-startup.ts index 3eca98ae13f..f33d081f2cc 100644 --- a/mobile/src/session/use-mobile-session-startup.ts +++ b/mobile/src/session/use-mobile-session-startup.ts @@ -95,6 +95,8 @@ export function useMobileSessionStartup(scope: MobileSessionKeyboardStateModel) worktreeId ]) + // Every setTimeout goes through addTimer into `timers`, which the returned cleanup clears. + // react-doctor-disable-next-line react-doctor/effect-needs-cleanup useEffect(() => { if (connState !== 'connected') { return diff --git a/mobile/src/session/use-mobile-session-terminal-runtime.ts b/mobile/src/session/use-mobile-session-terminal-runtime.ts index ab909254e10..8ef472fb845 100644 --- a/mobile/src/session/use-mobile-session-terminal-runtime.ts +++ b/mobile/src/session/use-mobile-session-terminal-runtime.ts @@ -76,7 +76,6 @@ export function useMobileSessionTerminalRuntime(scope: MobileSessionScreenStateM const activeHandleRef = useRef(null) const bufferedTerminalDraftState = useBufferedTerminalDrafts({ activeHandle, activeHandleRef }) const reconcileBufferedDraftsRef = useRef(bufferedTerminalDraftState.reconcileTerminalTabs) - reconcileBufferedDraftsRef.current = bufferedTerminalDraftState.reconcileTerminalTabs const activeSessionTabTypeRef = useRef(null) const pendingActiveSessionTabIdRef = useRef(null) const pendingActiveTerminalHandleRef = useRef(null) diff --git a/mobile/src/session/use-mobile-session-terminal-subscription-foundation.ts b/mobile/src/session/use-mobile-session-terminal-subscription-foundation.ts index 0289a1c65e9..76d8229b288 100644 --- a/mobile/src/session/use-mobile-session-terminal-subscription-foundation.ts +++ b/mobile/src/session/use-mobile-session-terminal-subscription-foundation.ts @@ -61,6 +61,8 @@ export function useMobileSessionTerminalSubscriptionFoundation( [clearNativeChatInputLease, nativeChatInputLeaseReadyRef, showNativeChatRef] ) const unsubscribeTerminalRef = useRef(unsubscribeTerminal) + // PTY event callbacks fire before passive effects flush, so they need the current unsubscribe. + // react-doctor-disable-next-line react-doctor/no-ref-current-in-render unsubscribeTerminalRef.current = unsubscribeTerminal const clearTerminalCache = useCallback(() => {