From 4c5271ba7dc2a2635f20f28d64415fc420aa6bbf Mon Sep 17 00:00:00 2001 From: Sumeru Chatterjee Date: Tue, 7 Apr 2026 16:14:17 +0100 Subject: [PATCH] fix: make @react-navigation/native an optional peer dependency (#770) The SDK imported `useIsFocused` directly from `@react-navigation/native` in the IterableInbox component, causing build failures for apps that do not use React Navigation. This change introduces a wrapper hook that tries to load `useIsFocused` from `@react-navigation/native` at module load time and falls back to always returning `true` when the package is not installed. The peer dependency is also marked as optional in peerDependenciesMeta. Co-Authored-By: Claude Opus 4.6 (1M context) --- package.json | 3 ++ src/core/hooks/index.ts | 1 + src/core/hooks/useIsFocused.ts | 45 ++++++++++++++++++++++++++ src/inbox/components/IterableInbox.tsx | 7 ++-- 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 src/core/hooks/useIsFocused.ts diff --git a/package.json b/package.json index 296749be5..dc4ab4592 100644 --- a/package.json +++ b/package.json @@ -118,6 +118,9 @@ "react-native-webview": "*" }, "peerDependenciesMeta": { + "@react-navigation/native": { + "optional": true + }, "expo": { "optional": true } diff --git a/src/core/hooks/index.ts b/src/core/hooks/index.ts index 35d77007a..5256bbbfc 100644 --- a/src/core/hooks/index.ts +++ b/src/core/hooks/index.ts @@ -1,2 +1,3 @@ export * from './useAppStateListener'; export * from './useDeviceOrientation'; +export * from './useIsFocused'; diff --git a/src/core/hooks/useIsFocused.ts b/src/core/hooks/useIsFocused.ts new file mode 100644 index 000000000..198f38622 --- /dev/null +++ b/src/core/hooks/useIsFocused.ts @@ -0,0 +1,45 @@ +/** + * The type of the `useIsFocused` hook from `@react-navigation/native`. + */ +type UseIsFocusedHook = () => boolean; + +let _useIsFocused: UseIsFocusedHook | undefined; + +/** + * Attempts to load `useIsFocused` from `@react-navigation/native`. + * + * This is done once at module load time. If the package is not installed, + * the import will fail and `_useIsFocused` will remain `undefined`. + */ +try { + // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports + _useIsFocused = require('@react-navigation/native').useIsFocused; +} catch { + // @react-navigation/native is not installed; this is fine. +} + +/** + * A fallback hook that always returns `true`. + * + * Used when `@react-navigation/native` is not installed. Since there is no + * navigation container managing focus, the component is always considered + * focused. + * + * @returns `true` + */ +function useAlwaysFocused(): boolean { + return true; +} + +/** + * A hook that returns whether the screen is currently focused. + * + * If `@react-navigation/native` is installed, this delegates to its + * `useIsFocused` hook. Otherwise, it falls back to always returning `true`, + * which is appropriate when no navigation container is in use (the component + * is always "focused" if there is no navigation stack managing focus). + * + * @returns `true` if the screen is focused (or if React Navigation is not + * installed), `false` otherwise. + */ +export const useIsFocused: UseIsFocusedHook = _useIsFocused ?? useAlwaysFocused; diff --git a/src/inbox/components/IterableInbox.tsx b/src/inbox/components/IterableInbox.tsx index 3cf44d829..b921911e5 100644 --- a/src/inbox/components/IterableInbox.tsx +++ b/src/inbox/components/IterableInbox.tsx @@ -1,4 +1,3 @@ -import { useIsFocused } from '@react-navigation/native'; import { useEffect, useState } from 'react'; import { Animated, @@ -11,7 +10,11 @@ import { import { SafeAreaView } from 'react-native-safe-area-context'; import RNIterableAPI from '../../api'; -import { useAppStateListener, useDeviceOrientation } from '../../core'; +import { + useAppStateListener, + useDeviceOrientation, + useIsFocused, +} from '../../core'; // expo throws an error if this is not imported directly due to circular // dependencies // See: https://github.com/expo/expo/issues/35100