@sigmela/router 0.3.4 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,9 +4,10 @@ import { StackRenderer } from "../StackRenderer.js";
4
4
  import { DrawerContext } from "./DrawerContext.js";
5
5
  import { useRouter } from "../RouterContext.js";
6
6
  import { ScreenStackItem } from 'react-native-screens';
7
- import { Pressable, StyleSheet, View, Text } from 'react-native';
7
+ import { Platform, Pressable, StyleSheet, View, Text } from 'react-native';
8
8
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
9
9
  import { useCallback, useSyncExternalStore, memo, useEffect, useState, useMemo, startTransition } from 'react';
10
+ const EMPTY_HISTORY = [];
10
11
  import Animated, { useSharedValue, useAnimatedStyle, withTiming, Easing } from 'react-native-reanimated';
11
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
13
  const TIMING_CONFIG = {
@@ -19,7 +20,10 @@ const DrawerStackRenderer = /*#__PURE__*/memo(({
19
20
  }) => {
20
21
  const router = useRouter();
21
22
  const stackId = stack.getId();
22
- const [history, setHistory] = useState(() => router.getStackHistory(stackId));
23
+
24
+ // On Android Fabric, start with empty history so the parent Screen's
25
+ // Fragment can attach before child ScreenStackItems mount.
26
+ const [history, setHistory] = useState(Platform.OS === 'android' ? EMPTY_HISTORY : () => router.getStackHistory(stackId));
23
27
  useEffect(() => {
24
28
  const update = () => {
25
29
  startTransition(() => {
@@ -4,9 +4,10 @@ import { ScreenStackItem } from "../ScreenStackItem/index.js";
4
4
  import { ScreenStack } from "../ScreenStack/index.js";
5
5
  import { SplitViewContext } from "./SplitViewContext.js";
6
6
  import { useRouter } from "../RouterContext.js";
7
- import { memo, useMemo, useState, useEffect, startTransition } from 'react';
8
- import { StyleSheet } from 'react-native';
7
+ import { memo, useMemo, useRef, useState, useEffect, startTransition } from 'react';
8
+ import { Platform, StyleSheet } from 'react-native';
9
9
  import { jsx as _jsx } from "react/jsx-runtime";
10
+ const EMPTY_HISTORY = [];
10
11
  /**
11
12
  * On native, SplitView renders primary and secondary screens in a SINGLE
12
13
  * ScreenStack to get native push/pop animations.
@@ -38,12 +39,15 @@ export const RenderSplitView = /*#__PURE__*/memo(({
38
39
  const primaryId = splitView.primary.getId();
39
40
  const secondaryId = splitView.secondary.getId();
40
41
 
41
- // Subscribe to both stacks via useEffect + startTransition.
42
- // startTransition defers the Fabric commit so the FragmentManager has
43
- // time to finish pending transactions between commits.
44
- const [primaryHistory, setPrimaryHistory] = useState(() => router.getStackHistory(primaryId));
45
- const [secondaryHistory, setSecondaryHistory] = useState(() => router.getStackHistory(secondaryId));
42
+ // On Android Fabric, start with empty history so the parent Screen's
43
+ // Fragment can attach before child ScreenStackItems mount. The useEffect
44
+ // below populates via startTransition on the next commit.
45
+ const isAndroid = Platform.OS === 'android';
46
+ const hydratedRef = useRef(!isAndroid);
47
+ const [primaryHistory, setPrimaryHistory] = useState(isAndroid ? EMPTY_HISTORY : () => router.getStackHistory(primaryId));
48
+ const [secondaryHistory, setSecondaryHistory] = useState(isAndroid ? EMPTY_HISTORY : () => router.getStackHistory(secondaryId));
46
49
  useEffect(() => {
50
+ hydratedRef.current = true;
47
51
  const updatePrimary = () => {
48
52
  startTransition(() => {
49
53
  setPrimaryHistory(router.getStackHistory(primaryId));
@@ -66,11 +70,14 @@ export const RenderSplitView = /*#__PURE__*/memo(({
66
70
  };
67
71
  }, [router, primaryId, secondaryId]);
68
72
 
69
- // Fallback: if primary is empty, seed with first route
73
+ // Fallback: if primary is empty, seed with first route.
74
+ // On Android, skip the seed during the initial empty mount (before useEffect
75
+ // hydrates history) so no ScreenStackItems mount before the parent Fragment attaches.
70
76
  const primaryHistoryToRender = useMemo(() => {
71
77
  if (primaryHistory.length > 0) {
72
78
  return primaryHistory;
73
79
  }
80
+ if (!hydratedRef.current) return [];
74
81
  const first = splitView.primary.getFirstRoute();
75
82
  if (!first) return [];
76
83
  const activePath = router.getActiveRoute()?.path;
@@ -7,6 +7,7 @@ import { Tabs } from 'react-native-screens';
7
7
  import { Platform, StyleSheet, View } from 'react-native';
8
8
  import { useCallback, useSyncExternalStore, memo, useEffect, useState, useMemo, startTransition } from 'react';
9
9
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
+ const EMPTY_HISTORY = [];
10
11
  const isImageSource = value => {
11
12
  if (value == null) return false;
12
13
  const valueType = typeof value;
@@ -136,7 +137,10 @@ const TabStackRenderer = /*#__PURE__*/memo(({
136
137
  }) => {
137
138
  const router = useRouter();
138
139
  const stackId = stack.getId();
139
- const [history, setHistory] = useState(() => router.getStackHistory(stackId));
140
+
141
+ // On Android Fabric, start with empty history so the parent Screen's
142
+ // Fragment can attach before child ScreenStackItems mount.
143
+ const [history, setHistory] = useState(Platform.OS === 'android' ? EMPTY_HISTORY : () => router.getStackHistory(stackId));
140
144
  useEffect(() => {
141
145
  const update = () => {
142
146
  startTransition(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigmela/router",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "React Native Router",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",