fix(native): make the server-URL check reactive so the reachability monitor and banner work after first setup

With the React Compiler, hasServerUrl read from MMKV during render was
memoized forever at the root. Read it through useSyncExternalStore instead.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
jakeandClaude Sonnet 5.5 committed 2026-10-05 12:54:17 -04:00
1 parent 8f4cd03715
commit b46bc4cdcb
4 files changed
+16 -7

No files matched your search

@@ -10,11 +10,11 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
import { ScaledIcon } from "@/components/ui/scaled-icon"; import { ScaledIcon } from "@/components/ui/scaled-icon";
import { Text } from "@/components/ui/text"; import { Text } from "@/components/ui/text";
import { queryClient } from "@/lib/query-client"; import { queryClient } from "@/lib/query-client";
import { authClient, hasStoredServerUrl, useServerReachability } from "@/lib/server"; import { authClient, useHasServerUrl, useServerReachability } from "@/lib/server";
import * as Haptics from "@/utils/haptics"; import * as Haptics from "@/utils/haptics";
export function ServerUnreachableBanner() { export function ServerUnreachableBanner() {
const hasServerUrl = !!process.env.EXPO_PUBLIC_SERVER_URL || hasStoredServerUrl(); const hasServerUrl = useHasServerUrl();
const { isReachable } = useServerReachability(); const { isReachable } = useServerReachability();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const wasReachable = useRef(true); const wasReachable = useRef(true);
@@ -9,11 +9,11 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
import { ScaledIcon } from "@/components/ui/scaled-icon"; import { ScaledIcon } from "@/components/ui/scaled-icon";
import { Text } from "@/components/ui/text"; import { Text } from "@/components/ui/text";
import { queryClient } from "@/lib/query-client"; import { queryClient } from "@/lib/query-client";
import { authClient, hasStoredServerUrl, useServerReachability } from "@/lib/server"; import { authClient, useHasServerUrl, useServerReachability } from "@/lib/server";
import * as Haptics from "@/utils/haptics"; import * as Haptics from "@/utils/haptics";
export function ServerUnreachableBanner() { export function ServerUnreachableBanner() {
const hasServerUrl = !!process.env.EXPO_PUBLIC_SERVER_URL || hasStoredServerUrl(); const hasServerUrl = useHasServerUrl();
const { isReachable } = useServerReachability(); const { isReachable } = useServerReachability();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const wasReachable = useRef(true); const wasReachable = useRef(true);
@@ -11,11 +11,11 @@ import {
consumeSessionRejected, consumeSessionRejected,
ensureInstanceId, ensureInstanceId,
getCurrentInstanceId, getCurrentInstanceId,
hasStoredServerUrl,
onServerReachabilityChange, onServerReachabilityChange,
onServerUrlChange, onServerUrlChange,
rebuildAuthClient, rebuildAuthClient,
startReachabilityMonitor, startReachabilityMonitor,
useHasServerUrl,
wasCachedSessionSeeded, wasCachedSessionSeeded,
} from "@/lib/server"; } from "@/lib/server";
import { toast } from "@/lib/toast"; import { toast } from "@/lib/toast";
@@ -44,7 +44,7 @@ export function useServerConnection() {
); );
const { data: session, isPending, isRefetching } = authClient.useSession(); const { data: session, isPending, isRefetching } = authClient.useSession();
const hasServerUrl = !!process.env.EXPO_PUBLIC_SERVER_URL || hasStoredServerUrl(); const hasServerUrl = useHasServerUrl();
// --- Instance ID resolution --- // --- Instance ID resolution ---
const [instanceId, setInstanceId] = useState(getCurrentInstanceId); const [instanceId, setInstanceId] = useState(getCurrentInstanceId);
+10 -1
View File
@@ -4,7 +4,7 @@ import { adminClient } from "better-auth/client/plugins";
import { createAuthClient } from "better-auth/react"; import { createAuthClient } from "better-auth/react";
import * as Network from "expo-network"; import * as Network from "expo-network";
import * as SecureStore from "expo-secure-store"; import * as SecureStore from "expo-secure-store";
import { useEffect, useState } from "react"; import { useEffect, useState, useSyncExternalStore } from "react";
import { AppState, type AppStateStatus } from "react-native"; import { AppState, type AppStateStatus } from "react-native";
import { globalStorage } from "@/lib/mmkv"; import { globalStorage } from "@/lib/mmkv";
@@ -102,6 +102,15 @@ export function hasStoredServerUrl(): boolean {
return globalStorage.contains(SERVER_URL_KEY); return globalStorage.contains(SERVER_URL_KEY);
} }
function hasServerUrlSnapshot(): boolean {
return !!process.env.EXPO_PUBLIC_SERVER_URL || hasStoredServerUrl();
}
/** Whether a server is configured; re-renders when the server URL changes (React-Compiler safe). */
export function useHasServerUrl(): boolean {
return useSyncExternalStore(onServerUrlChange, hasServerUrlSnapshot);
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Instance ID management // Instance ID management
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------