@react-navigation/core 8.0.0-alpha.0 → 8.0.0-alpha.10

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.
Files changed (168) hide show
  1. package/lib/module/BaseNavigationContainer.js +14 -7
  2. package/lib/module/BaseNavigationContainer.js.map +1 -1
  3. package/lib/module/ConsumedParamsContext.js +5 -0
  4. package/lib/module/ConsumedParamsContext.js.map +1 -0
  5. package/lib/module/NavigationBuilderContext.js +1 -0
  6. package/lib/module/NavigationBuilderContext.js.map +1 -1
  7. package/lib/module/NavigationIndependentTree.js +8 -4
  8. package/lib/module/NavigationIndependentTree.js.map +1 -1
  9. package/lib/module/NavigationMetaContext.js +15 -0
  10. package/lib/module/NavigationMetaContext.js.map +1 -0
  11. package/lib/module/NavigationProvider.js +14 -3
  12. package/lib/module/NavigationProvider.js.map +1 -1
  13. package/lib/module/NavigationStateContext.js.map +1 -1
  14. package/lib/module/PreventRemoveProvider.js +3 -3
  15. package/lib/module/PreventRemoveProvider.js.map +1 -1
  16. package/lib/module/SceneView.js +46 -56
  17. package/lib/module/SceneView.js.map +1 -1
  18. package/lib/module/StaticNavigation.js +30 -4
  19. package/lib/module/StaticNavigation.js.map +1 -1
  20. package/lib/module/createNavigatorFactory.js +22 -1
  21. package/lib/module/createNavigatorFactory.js.map +1 -1
  22. package/lib/module/getPathFromState.js +25 -3
  23. package/lib/module/getPathFromState.js.map +1 -1
  24. package/lib/module/getStateFromPath.js +158 -73
  25. package/lib/module/getStateFromPath.js.map +1 -1
  26. package/lib/module/getStateFromRouteParams.js +24 -0
  27. package/lib/module/getStateFromRouteParams.js.map +1 -0
  28. package/lib/module/index.js +2 -1
  29. package/lib/module/index.js.map +1 -1
  30. package/lib/module/theming/useTheme.js +1 -1
  31. package/lib/module/theming/useTheme.js.map +1 -1
  32. package/lib/module/types.js.map +1 -1
  33. package/lib/module/useCurrentRender.js +1 -1
  34. package/lib/module/useCurrentRender.js.map +1 -1
  35. package/lib/module/useDescriptors.js +12 -43
  36. package/lib/module/useDescriptors.js.map +1 -1
  37. package/lib/module/useFocusEvents.js +1 -1
  38. package/lib/module/useFocusEvents.js.map +1 -1
  39. package/lib/module/useFocusedListenersChildrenAdapter.js +1 -1
  40. package/lib/module/useFocusedListenersChildrenAdapter.js.map +1 -1
  41. package/lib/module/useIsFocused.js +7 -12
  42. package/lib/module/useIsFocused.js.map +1 -1
  43. package/lib/module/useNavigationBuilder.js +92 -42
  44. package/lib/module/useNavigationBuilder.js.map +1 -1
  45. package/lib/module/useNavigationCache.js +14 -56
  46. package/lib/module/useNavigationCache.js.map +1 -1
  47. package/lib/module/useNavigationHelpers.js +1 -1
  48. package/lib/module/useNavigationHelpers.js.map +1 -1
  49. package/lib/module/useNavigationIndependentTree.js +1 -1
  50. package/lib/module/useNavigationIndependentTree.js.map +1 -1
  51. package/lib/module/useNavigationState.js +4 -2
  52. package/lib/module/useNavigationState.js.map +1 -1
  53. package/lib/module/useOnAction.js +1 -1
  54. package/lib/module/useOnAction.js.map +1 -1
  55. package/lib/module/useOnGetState.js +2 -2
  56. package/lib/module/useOnGetState.js.map +1 -1
  57. package/lib/module/useOnPreventRemove.js +2 -2
  58. package/lib/module/useOnPreventRemove.js.map +1 -1
  59. package/lib/module/useOnRouteFocus.js +1 -1
  60. package/lib/module/useOnRouteFocus.js.map +1 -1
  61. package/lib/module/useOptionsGetters.js +2 -2
  62. package/lib/module/useOptionsGetters.js.map +1 -1
  63. package/lib/module/usePreventRemoveContext.js +1 -1
  64. package/lib/module/usePreventRemoveContext.js.map +1 -1
  65. package/lib/module/useRegisterNavigator.js +1 -1
  66. package/lib/module/useRegisterNavigator.js.map +1 -1
  67. package/lib/module/useScheduleUpdate.js +1 -1
  68. package/lib/module/useScheduleUpdate.js.map +1 -1
  69. package/lib/module/useStateForPath.js +1 -1
  70. package/lib/module/useStateForPath.js.map +1 -1
  71. package/lib/typescript/src/BaseNavigationContainer.d.ts +6 -3
  72. package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
  73. package/lib/typescript/src/ConsumedParamsContext.d.ts +8 -0
  74. package/lib/typescript/src/ConsumedParamsContext.d.ts.map +1 -0
  75. package/lib/typescript/src/NavigationBuilderContext.d.ts +11 -5
  76. package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
  77. package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts +4 -4
  78. package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts.map +1 -1
  79. package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
  80. package/lib/typescript/src/NavigationMetaContext.d.ts +12 -0
  81. package/lib/typescript/src/NavigationMetaContext.d.ts.map +1 -0
  82. package/lib/typescript/src/NavigationProvider.d.ts +4 -4
  83. package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
  84. package/lib/typescript/src/NavigationStateContext.d.ts +3 -3
  85. package/lib/typescript/src/NavigationStateContext.d.ts.map +1 -1
  86. package/lib/typescript/src/SceneView.d.ts.map +1 -1
  87. package/lib/typescript/src/StaticContainer.d.ts +1 -1
  88. package/lib/typescript/src/StaticContainer.d.ts.map +1 -1
  89. package/lib/typescript/src/StaticNavigation.d.ts +33 -19
  90. package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
  91. package/lib/typescript/src/createNavigatorFactory.d.ts +1 -1
  92. package/lib/typescript/src/createNavigatorFactory.d.ts.map +1 -1
  93. package/lib/typescript/src/findFocusedRoute.d.ts +3 -3
  94. package/lib/typescript/src/findFocusedRoute.d.ts.map +1 -1
  95. package/lib/typescript/src/getActionFromState.d.ts +3 -3
  96. package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
  97. package/lib/typescript/src/getPathFromState.d.ts +2 -2
  98. package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
  99. package/lib/typescript/src/getStateFromPath.d.ts +3 -3
  100. package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
  101. package/lib/typescript/src/getStateFromRouteParams.d.ts +3 -0
  102. package/lib/typescript/src/getStateFromRouteParams.d.ts.map +1 -0
  103. package/lib/typescript/src/index.d.ts +3 -1
  104. package/lib/typescript/src/index.d.ts.map +1 -1
  105. package/lib/typescript/src/types.d.ts +144 -82
  106. package/lib/typescript/src/types.d.ts.map +1 -1
  107. package/lib/typescript/src/useDescriptors.d.ts +12 -25
  108. package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
  109. package/lib/typescript/src/useIsFocused.d.ts +3 -0
  110. package/lib/typescript/src/useIsFocused.d.ts.map +1 -1
  111. package/lib/typescript/src/useNavigation.d.ts.map +1 -1
  112. package/lib/typescript/src/useNavigationBuilder.d.ts +20 -29
  113. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  114. package/lib/typescript/src/useNavigationCache.d.ts +2 -42
  115. package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
  116. package/lib/typescript/src/useNavigationHelpers.d.ts +18 -16
  117. package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
  118. package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
  119. package/lib/typescript/src/useOnAction.d.ts +6 -6
  120. package/lib/typescript/src/useOnAction.d.ts.map +1 -1
  121. package/lib/typescript/src/useOnRouteFocus.d.ts +6 -6
  122. package/lib/typescript/src/useOnRouteFocus.d.ts.map +1 -1
  123. package/lib/typescript/src/useRouteCache.d.ts +2 -2
  124. package/lib/typescript/src/useScheduleUpdate.d.ts.map +1 -1
  125. package/lib/typescript/src/utilities.d.ts +35 -3
  126. package/lib/typescript/src/utilities.d.ts.map +1 -1
  127. package/package.json +20 -18
  128. package/src/BaseNavigationContainer.tsx +332 -326
  129. package/src/ConsumedParamsContext.tsx +10 -0
  130. package/src/NavigationBuilderContext.tsx +14 -8
  131. package/src/NavigationFocusedRouteStateContext.tsx +4 -4
  132. package/src/NavigationIndependentTree.tsx +8 -5
  133. package/src/NavigationMetaContext.tsx +14 -0
  134. package/src/NavigationProvider.tsx +17 -3
  135. package/src/NavigationStateContext.tsx +5 -6
  136. package/src/PreventRemoveProvider.tsx +3 -3
  137. package/src/SceneView.tsx +58 -56
  138. package/src/StaticNavigation.tsx +100 -30
  139. package/src/createNavigatorFactory.tsx +35 -4
  140. package/src/findFocusedRoute.tsx +3 -3
  141. package/src/getActionFromState.tsx +7 -7
  142. package/src/getPathFromState.tsx +53 -9
  143. package/src/getStateFromPath.tsx +255 -97
  144. package/src/getStateFromRouteParams.tsx +60 -0
  145. package/src/index.tsx +2 -1
  146. package/src/theming/useTheme.tsx +1 -1
  147. package/src/types.tsx +330 -187
  148. package/src/useCurrentRender.tsx +1 -1
  149. package/src/useDescriptors.tsx +13 -48
  150. package/src/useFocusEvents.tsx +1 -1
  151. package/src/useFocusedListenersChildrenAdapter.tsx +1 -1
  152. package/src/useIsFocused.tsx +14 -21
  153. package/src/useNavigation.tsx +2 -2
  154. package/src/useNavigationBuilder.tsx +138 -53
  155. package/src/useNavigationCache.tsx +12 -73
  156. package/src/useNavigationHelpers.tsx +1 -1
  157. package/src/useNavigationIndependentTree.tsx +1 -1
  158. package/src/useNavigationState.tsx +6 -4
  159. package/src/useOnAction.tsx +8 -8
  160. package/src/useOnGetState.tsx +2 -2
  161. package/src/useOnPreventRemove.tsx +2 -2
  162. package/src/useOnRouteFocus.tsx +10 -12
  163. package/src/useOptionsGetters.tsx +2 -2
  164. package/src/usePreventRemoveContext.tsx +1 -1
  165. package/src/useRegisterNavigator.tsx +1 -1
  166. package/src/useScheduleUpdate.tsx +1 -3
  167. package/src/useStateForPath.tsx +1 -1
  168. package/src/utilities.tsx +72 -4
@@ -23,7 +23,7 @@ type Options = {
23
23
  * Mutating values like this is not safe in async mode, but it doesn't apply to SSR
24
24
  */
25
25
  export function useCurrentRender({ state, navigation, descriptors }: Options) {
26
- const current = React.useContext(CurrentRenderContext);
26
+ const current = React.use(CurrentRenderContext);
27
27
 
28
28
  if (current && navigation.isFocused()) {
29
29
  current.options = descriptors[state.routes[state.index].key].options;
@@ -67,7 +67,7 @@ type Options<
67
67
  ScreenOptions extends {},
68
68
  EventMap extends EventMapBase,
69
69
  > = {
70
- state: State;
70
+ routes: State['routes'];
71
71
  screens: Record<
72
72
  string,
73
73
  ScreenConfigWithParent<State, ScreenOptions, EventMap>
@@ -99,7 +99,7 @@ export function useDescriptors<
99
99
  ScreenOptions extends {},
100
100
  EventMap extends EventMapBase,
101
101
  >({
102
- state,
102
+ routes,
103
103
  screens,
104
104
  navigation,
105
105
  screenOptions,
@@ -113,17 +113,18 @@ export function useDescriptors<
113
113
  router,
114
114
  emitter,
115
115
  }: Options<State, ScreenOptions, EventMap>) {
116
- const theme = React.useContext(ThemeContext);
116
+ const theme = React.use(ThemeContext);
117
117
  const [options, setOptions] = React.useState<Record<string, ScreenOptions>>(
118
118
  {}
119
119
  );
120
120
  const {
121
121
  onDispatchAction,
122
+ onEmitEvent,
122
123
  onOptionsChange,
123
124
  scheduleUpdate,
124
125
  flushUpdates,
125
126
  stackRef,
126
- } = React.useContext(NavigationBuilderContext);
127
+ } = React.use(NavigationBuilderContext);
127
128
 
128
129
  const context = React.useMemo(
129
130
  () => ({
@@ -133,6 +134,7 @@ export function useDescriptors<
133
134
  addKeyedListener,
134
135
  onRouteFocus,
135
136
  onDispatchAction,
137
+ onEmitEvent,
136
138
  onOptionsChange,
137
139
  scheduleUpdate,
138
140
  flushUpdates,
@@ -145,6 +147,7 @@ export function useDescriptors<
145
147
  addKeyedListener,
146
148
  onRouteFocus,
147
149
  onDispatchAction,
150
+ onEmitEvent,
148
151
  onOptionsChange,
149
152
  scheduleUpdate,
150
153
  flushUpdates,
@@ -152,13 +155,13 @@ export function useDescriptors<
152
155
  ]
153
156
  );
154
157
 
155
- const { base, navigations } = useNavigationCache<
158
+ const navigations = useNavigationCache<
156
159
  State,
157
160
  ScreenOptions,
158
161
  EventMap,
159
162
  ActionHelpers
160
163
  >({
161
- state,
164
+ routes,
162
165
  getState,
163
166
  navigation,
164
167
  setOptions,
@@ -166,7 +169,7 @@ export function useDescriptors<
166
169
  emitter,
167
170
  });
168
171
 
169
- const routes = useRouteCache(state.routes);
172
+ const cachedRoutes = useRouteCache(routes);
170
173
 
171
174
  const getOptions = (
172
175
  route: RouteProp<ParamListBase, string>,
@@ -273,7 +276,7 @@ export function useDescriptors<
273
276
  );
274
277
  };
275
278
 
276
- const descriptors = routes.reduce<
279
+ const descriptors = cachedRoutes.reduce<
277
280
  Record<
278
281
  string,
279
282
  Descriptor<
@@ -292,12 +295,7 @@ export function useDescriptors<
292
295
  >((acc, route, i) => {
293
296
  const navigation = navigations[route.key];
294
297
  const customOptions = getOptions(route, navigation, options[route.key]);
295
- const element = render(
296
- route,
297
- navigation,
298
- customOptions,
299
- state.routes[i].state
300
- );
298
+ const element = render(route, navigation, customOptions, routes[i]?.state);
301
299
 
302
300
  acc[route.key] = {
303
301
  route,
@@ -311,38 +309,5 @@ export function useDescriptors<
311
309
  return acc;
312
310
  }, {});
313
311
 
314
- /**
315
- * Create a descriptor object for a route.
316
- *
317
- * @param route Route object for which the descriptor should be created
318
- * @param placeholder Whether the descriptor should be a placeholder, e.g. for a route not yet in the state
319
- * @returns Descriptor object
320
- */
321
- const describe = (route: RouteProp<ParamListBase>, placeholder: boolean) => {
322
- if (!placeholder) {
323
- if (!(route.key in descriptors)) {
324
- throw new Error(`Couldn't find a route with the key ${route.key}.`);
325
- }
326
-
327
- return descriptors[route.key];
328
- }
329
-
330
- const navigation = base;
331
- const customOptions = getOptions(route, navigation, {});
332
- const element = render(route, navigation, customOptions, undefined);
333
-
334
- return {
335
- route,
336
- navigation,
337
- render() {
338
- return element;
339
- },
340
- options: customOptions as ScreenOptions,
341
- };
342
- };
343
-
344
- return {
345
- describe,
346
- descriptors,
347
- };
312
+ return descriptors;
348
313
  }
@@ -17,7 +17,7 @@ export function useFocusEvents<State extends NavigationState>({
17
17
  state,
18
18
  emitter,
19
19
  }: Options<State>) {
20
- const navigation = React.useContext(NavigationContext);
20
+ const navigation = React.use(NavigationContext);
21
21
  const lastFocusedKeyRef = React.useRef<string | undefined>(undefined);
22
22
 
23
23
  const currentFocusedKey = state.routes[state.index].key;
@@ -20,7 +20,7 @@ export function useFocusedListenersChildrenAdapter({
20
20
  navigation,
21
21
  focusedListeners,
22
22
  }: Options) {
23
- const { addListener } = React.useContext(NavigationBuilderContext);
23
+ const { addListener } = React.use(NavigationBuilderContext);
24
24
 
25
25
  const listener = React.useCallback(
26
26
  (callback: FocusedNavigationCallback<any>) => {
@@ -1,32 +1,25 @@
1
1
  import * as React from 'react';
2
2
 
3
- import { useNavigation } from './useNavigation';
3
+ export const FocusedRouteKeyContext = React.createContext<string | undefined>(
4
+ undefined
5
+ );
6
+
7
+ export const IsFocusedContext = React.createContext<boolean | undefined>(
8
+ undefined
9
+ );
4
10
 
5
11
  /**
6
12
  * Hook to get the current focus state of the screen. Returns a `true` if screen is focused, otherwise `false`.
7
13
  * This can be used if a component needs to render something based on the focus state.
8
14
  */
9
15
  export function useIsFocused(): boolean {
10
- const navigation = useNavigation();
11
-
12
- const subscribe = React.useCallback(
13
- (callback: () => void) => {
14
- const unsubscribeFocus = navigation.addListener('focus', callback);
15
- const unsubscribeBlur = navigation.addListener('blur', callback);
16
-
17
- return () => {
18
- unsubscribeFocus();
19
- unsubscribeBlur();
20
- };
21
- },
22
- [navigation]
23
- );
16
+ const isFocused = React.use(IsFocusedContext);
24
17
 
25
- const value = React.useSyncExternalStore(
26
- subscribe,
27
- navigation.isFocused,
28
- navigation.isFocused
29
- );
18
+ if (isFocused === undefined) {
19
+ throw new Error(
20
+ "Couldn't determine focus state. Is your component inside a screen in a navigator?"
21
+ );
22
+ }
30
23
 
31
- return value;
24
+ return isFocused;
32
25
  }
@@ -27,8 +27,8 @@ export function useNavigation<
27
27
  >(): NavigationListForNavigator<Navigator>[keyof NavigationListForNavigator<Navigator>];
28
28
  export function useNavigation<
29
29
  const Navigator = RootNavigator,
30
- const RouteName extends
31
- keyof NavigationListForNested<Navigator> = keyof NavigationListForNested<Navigator>,
30
+ const RouteName extends keyof NavigationListForNested<Navigator> =
31
+ keyof NavigationListForNested<Navigator>,
32
32
  >(name: RouteName): NavigationListForNested<Navigator>[RouteName];
33
33
  export function useNavigation(name?: string): unknown {
34
34
  const root = React.use(NavigationContainerRefContext);
@@ -15,11 +15,14 @@ import * as React from 'react';
15
15
  import { isValidElementType } from 'react-is';
16
16
  import useLatestCallback from 'use-latest-callback';
17
17
 
18
+ import { ConsumedParamsContext } from './ConsumedParamsContext';
18
19
  import { deepFreeze } from './deepFreeze';
19
20
  import { Group } from './Group';
20
21
  import { isArrayEqual } from './isArrayEqual';
21
22
  import { isRecordEqual } from './isRecordEqual';
23
+ import { NavigationBuilderContext } from './NavigationBuilderContext';
22
24
  import { NavigationHelpersContext } from './NavigationHelpersContext';
25
+ import { NavigationMetaContext } from './NavigationMetaContext';
23
26
  import { NavigationRouteContext } from './NavigationProvider';
24
27
  import { NavigationStateContext } from './NavigationStateContext';
25
28
  import { PreventRemoveProvider } from './PreventRemoveProvider';
@@ -40,6 +43,7 @@ import { type ScreenConfigWithParent, useDescriptors } from './useDescriptors';
40
43
  import { useEventEmitter } from './useEventEmitter';
41
44
  import { useFocusedListenersChildrenAdapter } from './useFocusedListenersChildrenAdapter';
42
45
  import { useFocusEvents } from './useFocusEvents';
46
+ import { FocusedRouteKeyContext } from './useIsFocused';
43
47
  import { useKeyedChildListeners } from './useKeyedChildListeners';
44
48
  import { useLazyValue } from './useLazyValue';
45
49
  import { useNavigationHelpers } from './useNavigationHelpers';
@@ -56,7 +60,7 @@ PrivateValueStore;
56
60
 
57
61
  type NavigatorRoute = {
58
62
  key: string;
59
- params?: NavigatorScreenParams<ParamListBase>;
63
+ params?: NavigatorScreenParams<ParamListBase> | undefined;
60
64
  };
61
65
 
62
66
  const isScreen = (
@@ -318,9 +322,14 @@ export function useNavigationBuilder<
318
322
  ) {
319
323
  const navigatorKey = useRegisterNavigator();
320
324
 
321
- const route = React.useContext(NavigationRouteContext) as
322
- | NavigatorRoute
323
- | undefined;
325
+ const route = React.use(NavigationRouteContext) as NavigatorRoute | undefined;
326
+
327
+ const consumedParams = React.use(ConsumedParamsContext);
328
+
329
+ const isNestedParamsConsumed =
330
+ typeof route?.params === 'object' && route.params != null
331
+ ? consumedParams?.isConsumed(route.params)
332
+ : false;
324
333
 
325
334
  const {
326
335
  children,
@@ -416,7 +425,9 @@ export function useNavigationBuilder<
416
425
  );
417
426
 
418
427
  const isStateInitialized = React.useCallback(
419
- (state: NavigationState | PartialState<NavigationState> | undefined) =>
428
+ <T extends NavigationState>(
429
+ state: T | PartialState<T> | undefined
430
+ ): state is T =>
420
431
  state !== undefined && state.stale === false && isStateValid(state),
421
432
  [isStateValid]
422
433
  );
@@ -434,16 +445,22 @@ export function useNavigationBuilder<
434
445
  setKey,
435
446
  getKey,
436
447
  getIsInitial,
437
- } = React.useContext(NavigationStateContext);
448
+ } = React.use(NavigationStateContext);
438
449
 
439
- const stateCleanedUp = React.useRef(false);
450
+ const stateCleanupRef = React.useRef<boolean>(false);
451
+ const lastStateRef = React.useRef<State | PartialState<State> | undefined>(
452
+ undefined
453
+ );
440
454
 
441
455
  const setState = useLatestCallback(
442
- (state: NavigationState | PartialState<NavigationState> | undefined) => {
443
- if (stateCleanedUp.current) {
456
+ (state: State | PartialState<State> | undefined) => {
457
+ if (stateCleanupRef.current) {
458
+ // Store the state locally in case the current navigator is in `Activity`
459
+ lastStateRef.current = state;
460
+
444
461
  // State might have been already cleaned up due to unmount
445
- // We do not want to expose API allowing to override this
446
- // This would lead to old data preservation on main navigator unmount
462
+ // We don't want to update `route.state` in parent
463
+ // Otherwise it will be reused if a new navigator gets mounted
447
464
  return;
448
465
  }
449
466
 
@@ -455,11 +472,32 @@ export function useNavigationBuilder<
455
472
  stateBeforeInitialization,
456
473
  initializedState,
457
474
  isFirstStateInitialization,
475
+ paramsUsedForInitialization,
458
476
  ] = React.useMemo((): [
459
477
  PartialState<State> | undefined,
460
478
  State | undefined,
461
479
  boolean,
480
+ object | undefined,
462
481
  ] => {
482
+ // If the state was already cleaned up, but we have it stored in ref,
483
+ // It likely got cleaned up due to `<Activity mode="hidden">`
484
+ // We should reuse this state to avoid remounting screens
485
+ if (
486
+ stateCleanupRef.current &&
487
+ lastStateRef.current &&
488
+ isStateValid(lastStateRef.current)
489
+ ) {
490
+ const state: State = isStateInitialized(lastStateRef.current)
491
+ ? lastStateRef.current
492
+ : router.getRehydratedState(lastStateRef.current, {
493
+ routeNames,
494
+ routeParamList,
495
+ routeGetIdList,
496
+ });
497
+
498
+ return [undefined, state, false, undefined];
499
+ }
500
+
463
501
  const initialRouteParamList = routeNames.reduce<
464
502
  Record<string, object | undefined>
465
503
  >((acc, curr) => {
@@ -492,7 +530,8 @@ export function useNavigationBuilder<
492
530
  !(
493
531
  typeof route?.params?.screen === 'string' &&
494
532
  route?.params?.initial !== false
495
- )
533
+ ) &&
534
+ !isNestedParamsConsumed
496
535
  ) {
497
536
  return [
498
537
  undefined,
@@ -502,28 +541,40 @@ export function useNavigationBuilder<
502
541
  routeGetIdList,
503
542
  }),
504
543
  true,
544
+ undefined,
505
545
  ];
506
546
  } else {
507
- const stateFromParams = getStateFromParams(route?.params);
508
- const stateBeforeInitialization = (stateFromParams ??
509
- currentState) as PartialState<State>;
510
- const hydratedState = router.getRehydratedState(
511
- stateBeforeInitialization,
512
- {
513
- routeNames,
514
- routeParamList: initialRouteParamList,
515
- routeGetIdList,
516
- }
517
- );
547
+ const paramsForState = isNestedParamsConsumed ? undefined : route?.params;
548
+ const stateFromParams = paramsForState
549
+ ? getStateFromParams(paramsForState)
550
+ : undefined;
551
+
552
+ const stateBeforeInitialization = (stateFromParams ?? currentState) as
553
+ | PartialState<State>
554
+ | undefined;
555
+
556
+ const hydratedState =
557
+ stateBeforeInitialization == null
558
+ ? router.getInitialState({
559
+ routeNames,
560
+ routeParamList: initialRouteParamList,
561
+ routeGetIdList,
562
+ })
563
+ : router.getRehydratedState(stateBeforeInitialization, {
564
+ routeNames,
565
+ routeParamList: initialRouteParamList,
566
+ routeGetIdList,
567
+ });
518
568
 
519
569
  if (
570
+ stateBeforeInitialization != null &&
520
571
  options.routeNamesChangeBehavior === 'lastUnhandled' &&
521
572
  doesStateHaveOnlyInvalidRoutes(stateBeforeInitialization)
522
573
  ) {
523
- return [stateBeforeInitialization, hydratedState, true];
574
+ return [stateBeforeInitialization, hydratedState, true, paramsForState];
524
575
  }
525
576
 
526
- return [undefined, hydratedState, false];
577
+ return [undefined, hydratedState, false, paramsForState];
527
578
  }
528
579
  // We explicitly don't include routeNames, route.params etc. in the dep list
529
580
  // below. We want to avoid forcing a new state to be calculated in those cases
@@ -601,22 +652,18 @@ export function useNavigationBuilder<
601
652
  });
602
653
  }
603
654
 
604
- const previousNestedParamsRef = React.useRef(route?.params);
605
-
606
- React.useEffect(() => {
607
- previousNestedParamsRef.current = route?.params;
608
- }, [route?.params]);
609
-
610
- if (route?.params) {
611
- const previousParams = previousNestedParamsRef.current;
655
+ let didConsumeNestedParams = route?.params === paramsUsedForInitialization;
612
656
 
657
+ if (route?.params && !didConsumeNestedParams) {
613
658
  let action: CommonActions.Action | undefined;
614
659
 
615
660
  if (
616
661
  typeof route.params.state === 'object' &&
617
662
  route.params.state != null &&
618
- route.params !== previousParams
663
+ !isNestedParamsConsumed
619
664
  ) {
665
+ didConsumeNestedParams = true;
666
+
620
667
  if (
621
668
  options.routeNamesChangeBehavior === 'lastUnhandled' &&
622
669
  doesStateHaveOnlyInvalidRoutes(route.params.state)
@@ -631,8 +678,10 @@ export function useNavigationBuilder<
631
678
  } else if (
632
679
  typeof route.params.screen === 'string' &&
633
680
  ((route.params.initial === false && isFirstStateInitialization) ||
634
- route.params !== previousParams)
681
+ !isNestedParamsConsumed)
635
682
  ) {
683
+ didConsumeNestedParams = true;
684
+
636
685
  if (
637
686
  options.routeNamesChangeBehavior === 'lastUnhandled' &&
638
687
  !routeNames.includes(route.params.screen)
@@ -673,14 +722,24 @@ export function useNavigationBuilder<
673
722
  : nextState;
674
723
  }
675
724
 
676
- const shouldUpdate =
677
- state !== nextState ||
678
- typeof route?.params?.state === 'object' ||
679
- typeof route?.params?.screen === 'string';
725
+ const setConsumedParams = consumedParams?.setConsumed;
726
+
727
+ React.useEffect(() => {
728
+ if (
729
+ setConsumedParams &&
730
+ didConsumeNestedParams &&
731
+ typeof route?.params === 'object' &&
732
+ route.params != null
733
+ ) {
734
+ setConsumedParams(route.params);
735
+ }
736
+ }, [didConsumeNestedParams, route?.params, setConsumedParams]);
737
+
738
+ const shouldUpdate = state !== nextState;
680
739
 
681
740
  useScheduleUpdate(() => {
682
741
  if (shouldUpdate) {
683
- // If the state needs to be updated, we'll schedule an update
742
+ // Schedule an update if the state needs to be updated
684
743
  setState(nextState);
685
744
 
686
745
  if (shouldClearUnhandledState) {
@@ -694,25 +753,35 @@ export function useNavigationBuilder<
694
753
  // So we override the state object we return to use the latest state as soon as possible
695
754
  state = nextState;
696
755
 
756
+ // Last state to reuse if component gets cleaned up due to `<Activity mode="hidden">`
757
+ React.useEffect(() => {
758
+ lastStateRef.current = state;
759
+ });
760
+
761
+ const lastNotifiedStateRef = React.useRef<State | null>(null);
762
+
697
763
  React.useEffect(() => {
698
764
  // In strict mode, React will double-invoke effects.
699
765
  // So we need to reset the flag if component was not unmounted
700
- stateCleanedUp.current = false;
766
+ stateCleanupRef.current = false;
701
767
 
702
768
  setKey(navigatorKey);
703
769
 
704
- if (!getIsInitial()) {
770
+ if (!getIsInitial() && lastNotifiedStateRef.current !== state) {
705
771
  // If it's not initial render, we need to update the state
706
772
  // This will make sure that our container gets notifier of state changes due to new mounts
707
773
  // This is necessary for proper screen tracking, URL updates etc.
708
- setState(nextState);
774
+ // We only notify if the state is different what we already notified
775
+ // Otherwise this goes into a loop when inside `<Activity mode="hidden">`
776
+ setState(state);
777
+ lastNotifiedStateRef.current = state;
709
778
  }
710
779
 
711
780
  return () => {
712
781
  // We need to clean up state for this navigator on unmount
713
782
  if (getCurrentState() !== undefined && getKey() === navigatorKey) {
714
783
  setCurrentState(undefined);
715
- stateCleanedUp.current = true;
784
+ stateCleanupRef.current = true;
716
785
  }
717
786
  };
718
787
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -741,6 +810,8 @@ export function useNavigationBuilder<
741
810
  );
742
811
  });
743
812
 
813
+ const { onEmitEvent } = React.use(NavigationBuilderContext);
814
+
744
815
  const emitter = useEventEmitter<EventMapCore<State>>((e) => {
745
816
  const routeNames = [];
746
817
 
@@ -792,6 +863,13 @@ export function useNavigationBuilder<
792
863
  .filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
793
864
 
794
865
  listeners.forEach((listener) => listener?.(e));
866
+
867
+ onEmitEvent({
868
+ type: e.type,
869
+ data: e.data,
870
+ target: e.target,
871
+ defaultPrevented: e.defaultPrevented,
872
+ });
795
873
  });
796
874
 
797
875
  useFocusEvents({ state, emitter });
@@ -826,7 +904,7 @@ export function useNavigationBuilder<
826
904
  setState,
827
905
  });
828
906
 
829
- const onUnhandledActionParent = React.useContext(UnhandledActionContext);
907
+ const onUnhandledActionParent = React.use(UnhandledActionContext);
830
908
 
831
909
  const onUnhandledAction = useLatestCallback((action: NavigationAction) => {
832
910
  if (
@@ -886,13 +964,15 @@ export function useNavigationBuilder<
886
964
  getStateListeners: keyedListeners.getState,
887
965
  });
888
966
 
889
- const { describe, descriptors } = useDescriptors<
967
+ const descriptors = useDescriptors<
890
968
  State,
891
969
  ActionHelpers,
892
970
  ScreenOptions,
893
971
  EventMap
894
972
  >({
895
- state,
973
+ routes: router.getRoutesFromState
974
+ ? router.getRoutesFromState(state)
975
+ : state.routes,
896
976
  screens,
897
977
  navigation,
898
978
  screenOptions,
@@ -926,18 +1006,23 @@ export function useNavigationBuilder<
926
1006
  : children;
927
1007
 
928
1008
  return (
929
- <NavigationHelpersContext.Provider value={navigation}>
930
- <NavigationStateListenerProvider state={state}>
931
- <PreventRemoveProvider>{element}</PreventRemoveProvider>
932
- </NavigationStateListenerProvider>
933
- </NavigationHelpersContext.Provider>
1009
+ <NavigationMetaContext.Provider value={undefined}>
1010
+ <NavigationHelpersContext.Provider value={navigation}>
1011
+ <NavigationStateListenerProvider state={state}>
1012
+ <FocusedRouteKeyContext.Provider
1013
+ value={state.routes[state.index].key}
1014
+ >
1015
+ <PreventRemoveProvider>{element}</PreventRemoveProvider>
1016
+ </FocusedRouteKeyContext.Provider>
1017
+ </NavigationStateListenerProvider>
1018
+ </NavigationHelpersContext.Provider>
1019
+ </NavigationMetaContext.Provider>
934
1020
  );
935
1021
  });
936
1022
 
937
1023
  return {
938
1024
  state,
939
1025
  navigation,
940
- describe,
941
1026
  descriptors,
942
1027
  NavigationContent,
943
1028
  };