@react-navigation/core 8.0.0-alpha.2 → 8.0.0-alpha.20

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 (251) hide show
  1. package/lib/module/BaseNavigationContainer.js +20 -9
  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/NavigationProvider.js +14 -3
  10. package/lib/module/NavigationProvider.js.map +1 -1
  11. package/lib/module/NavigationStateContext.js.map +1 -1
  12. package/lib/module/PreventRemoveProvider.js +3 -3
  13. package/lib/module/PreventRemoveProvider.js.map +1 -1
  14. package/lib/module/SceneView.js +46 -56
  15. package/lib/module/SceneView.js.map +1 -1
  16. package/lib/module/StaticNavigation.js +166 -46
  17. package/lib/module/StaticNavigation.js.map +1 -1
  18. package/lib/module/checkSerializable.js +11 -4
  19. package/lib/module/checkSerializable.js.map +1 -1
  20. package/lib/module/createNavigatorFactory.js +27 -5
  21. package/lib/module/createNavigatorFactory.js.map +1 -1
  22. package/lib/module/findFocusedRoute.js +7 -4
  23. package/lib/module/findFocusedRoute.js.map +1 -1
  24. package/lib/module/getActionFromState.js +40 -14
  25. package/lib/module/getActionFromState.js.map +1 -1
  26. package/lib/module/getPathFromState.js +48 -11
  27. package/lib/module/getPathFromState.js.map +1 -1
  28. package/lib/module/getPatternParts.js +3 -0
  29. package/lib/module/getPatternParts.js.map +1 -1
  30. package/lib/module/getStateFromPath.js +353 -166
  31. package/lib/module/getStateFromPath.js.map +1 -1
  32. package/lib/module/getStateFromRouteParams.js +24 -0
  33. package/lib/module/getStateFromRouteParams.js.map +1 -0
  34. package/lib/module/index.js +3 -4
  35. package/lib/module/index.js.map +1 -1
  36. package/lib/module/theming/useTheme.js +1 -1
  37. package/lib/module/theming/useTheme.js.map +1 -1
  38. package/lib/module/types.js +15 -0
  39. package/lib/module/types.js.map +1 -1
  40. package/lib/module/useDescriptors.js +18 -9
  41. package/lib/module/useDescriptors.js.map +1 -1
  42. package/lib/module/useEventEmitter.js +43 -31
  43. package/lib/module/useEventEmitter.js.map +1 -1
  44. package/lib/module/useFocusEvents.js +6 -2
  45. package/lib/module/useFocusEvents.js.map +1 -1
  46. package/lib/module/useFocusedListenersChildrenAdapter.js +2 -2
  47. package/lib/module/useFocusedListenersChildrenAdapter.js.map +1 -1
  48. package/lib/module/useIsFocused.js +7 -12
  49. package/lib/module/useIsFocused.js.map +1 -1
  50. package/lib/module/useNavigation.js.map +1 -1
  51. package/lib/module/useNavigationBuilder.js +144 -86
  52. package/lib/module/useNavigationBuilder.js.map +1 -1
  53. package/lib/module/useNavigationCache.js +3 -3
  54. package/lib/module/useNavigationCache.js.map +1 -1
  55. package/lib/module/useNavigationHelpers.js +1 -1
  56. package/lib/module/useNavigationHelpers.js.map +1 -1
  57. package/lib/module/useNavigationIndependentTree.js +1 -1
  58. package/lib/module/useNavigationIndependentTree.js.map +1 -1
  59. package/lib/module/useNavigationState.js +6 -4
  60. package/lib/module/useNavigationState.js.map +1 -1
  61. package/lib/module/useOnAction.js +3 -3
  62. package/lib/module/useOnAction.js.map +1 -1
  63. package/lib/module/useOnGetState.js +6 -3
  64. package/lib/module/useOnGetState.js.map +1 -1
  65. package/lib/module/useOnPreventRemove.js +3 -3
  66. package/lib/module/useOnPreventRemove.js.map +1 -1
  67. package/lib/module/useOnRouteFocus.js +1 -1
  68. package/lib/module/useOnRouteFocus.js.map +1 -1
  69. package/lib/module/useOptionsGetters.js +7 -4
  70. package/lib/module/useOptionsGetters.js.map +1 -1
  71. package/lib/module/usePreventRemove.js +1 -1
  72. package/lib/module/usePreventRemove.js.map +1 -1
  73. package/lib/module/usePreventRemoveContext.js +1 -1
  74. package/lib/module/usePreventRemoveContext.js.map +1 -1
  75. package/lib/module/useRegisterNavigator.js +2 -2
  76. package/lib/module/useRegisterNavigator.js.map +1 -1
  77. package/lib/module/useRoute.js.map +1 -1
  78. package/lib/module/useRouteCache.js +2 -1
  79. package/lib/module/useRouteCache.js.map +1 -1
  80. package/lib/module/useScheduleUpdate.js +1 -1
  81. package/lib/module/useScheduleUpdate.js.map +1 -1
  82. package/lib/module/useStateForPath.js +1 -1
  83. package/lib/module/useStateForPath.js.map +1 -1
  84. package/lib/module/validatePathConfig.js +2 -1
  85. package/lib/module/validatePathConfig.js.map +1 -1
  86. package/lib/typescript/src/BaseNavigationContainer.d.ts +6 -3
  87. package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
  88. package/lib/typescript/src/ConsumedParamsContext.d.ts +8 -0
  89. package/lib/typescript/src/ConsumedParamsContext.d.ts.map +1 -0
  90. package/lib/typescript/src/EnsureSingleNavigator.d.ts +1 -1
  91. package/lib/typescript/src/EnsureSingleNavigator.d.ts.map +1 -1
  92. package/lib/typescript/src/Group.d.ts +1 -1
  93. package/lib/typescript/src/Group.d.ts.map +1 -1
  94. package/lib/typescript/src/NavigationBuilderContext.d.ts +12 -6
  95. package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
  96. package/lib/typescript/src/NavigationContainerRefContext.d.ts +1 -1
  97. package/lib/typescript/src/NavigationContainerRefContext.d.ts.map +1 -1
  98. package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts +4 -4
  99. package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts.map +1 -1
  100. package/lib/typescript/src/NavigationHelpersContext.d.ts +1 -1
  101. package/lib/typescript/src/NavigationHelpersContext.d.ts.map +1 -1
  102. package/lib/typescript/src/NavigationIndependentTree.d.ts +1 -1
  103. package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
  104. package/lib/typescript/src/NavigationProvider.d.ts +10 -10
  105. package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
  106. package/lib/typescript/src/NavigationStateContext.d.ts +3 -3
  107. package/lib/typescript/src/NavigationStateContext.d.ts.map +1 -1
  108. package/lib/typescript/src/PreventRemoveProvider.d.ts +1 -1
  109. package/lib/typescript/src/PreventRemoveProvider.d.ts.map +1 -1
  110. package/lib/typescript/src/SceneView.d.ts +3 -2
  111. package/lib/typescript/src/SceneView.d.ts.map +1 -1
  112. package/lib/typescript/src/Screen.d.ts +1 -1
  113. package/lib/typescript/src/Screen.d.ts.map +1 -1
  114. package/lib/typescript/src/StaticContainer.d.ts +1 -1
  115. package/lib/typescript/src/StaticContainer.d.ts.map +1 -1
  116. package/lib/typescript/src/StaticNavigation.d.ts +123 -92
  117. package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
  118. package/lib/typescript/src/checkSerializable.d.ts +5 -3
  119. package/lib/typescript/src/checkSerializable.d.ts.map +1 -1
  120. package/lib/typescript/src/createNavigationContainerRef.d.ts +1 -1
  121. package/lib/typescript/src/createNavigationContainerRef.d.ts.map +1 -1
  122. package/lib/typescript/src/createNavigatorFactory.d.ts +17 -5
  123. package/lib/typescript/src/createNavigatorFactory.d.ts.map +1 -1
  124. package/lib/typescript/src/findFocusedRoute.d.ts +3 -3
  125. package/lib/typescript/src/findFocusedRoute.d.ts.map +1 -1
  126. package/lib/typescript/src/getActionFromState.d.ts +4 -4
  127. package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
  128. package/lib/typescript/src/getPathFromState.d.ts +3 -3
  129. package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
  130. package/lib/typescript/src/getPatternParts.d.ts +1 -0
  131. package/lib/typescript/src/getPatternParts.d.ts.map +1 -1
  132. package/lib/typescript/src/getStateFromPath.d.ts +5 -5
  133. package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
  134. package/lib/typescript/src/getStateFromRouteParams.d.ts +3 -0
  135. package/lib/typescript/src/getStateFromRouteParams.d.ts.map +1 -0
  136. package/lib/typescript/src/index.d.ts +34 -33
  137. package/lib/typescript/src/index.d.ts.map +1 -1
  138. package/lib/typescript/src/theming/ThemeContext.d.ts +1 -1
  139. package/lib/typescript/src/theming/ThemeContext.d.ts.map +1 -1
  140. package/lib/typescript/src/theming/ThemeProvider.d.ts +2 -2
  141. package/lib/typescript/src/theming/ThemeProvider.d.ts.map +1 -1
  142. package/lib/typescript/src/theming/useTheme.d.ts +1 -1
  143. package/lib/typescript/src/theming/useTheme.d.ts.map +1 -1
  144. package/lib/typescript/src/types.d.ts +215 -131
  145. package/lib/typescript/src/types.d.ts.map +1 -1
  146. package/lib/typescript/src/useChildListeners.d.ts +3 -3
  147. package/lib/typescript/src/useChildListeners.d.ts.map +1 -1
  148. package/lib/typescript/src/useComponent.d.ts +1 -1
  149. package/lib/typescript/src/useComponent.d.ts.map +1 -1
  150. package/lib/typescript/src/useDescriptors.d.ts +7 -7
  151. package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
  152. package/lib/typescript/src/useEventEmitter.d.ts +1 -1
  153. package/lib/typescript/src/useEventEmitter.d.ts.map +1 -1
  154. package/lib/typescript/src/useFocusEvents.d.ts +2 -2
  155. package/lib/typescript/src/useFocusEvents.d.ts.map +1 -1
  156. package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts +2 -2
  157. package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts.map +1 -1
  158. package/lib/typescript/src/useIsFocused.d.ts +3 -0
  159. package/lib/typescript/src/useIsFocused.d.ts.map +1 -1
  160. package/lib/typescript/src/useKeyedChildListeners.d.ts +3 -3
  161. package/lib/typescript/src/useKeyedChildListeners.d.ts.map +1 -1
  162. package/lib/typescript/src/useNavigation.d.ts +5 -3
  163. package/lib/typescript/src/useNavigation.d.ts.map +1 -1
  164. package/lib/typescript/src/useNavigationBuilder.d.ts +24 -24
  165. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  166. package/lib/typescript/src/useNavigationCache.d.ts +3 -3
  167. package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
  168. package/lib/typescript/src/useNavigationContainerRef.d.ts +1 -1
  169. package/lib/typescript/src/useNavigationContainerRef.d.ts.map +1 -1
  170. package/lib/typescript/src/useNavigationHelpers.d.ts +19 -19
  171. package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
  172. package/lib/typescript/src/useNavigationState.d.ts +2 -2
  173. package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
  174. package/lib/typescript/src/useOnAction.d.ts +9 -9
  175. package/lib/typescript/src/useOnAction.d.ts.map +1 -1
  176. package/lib/typescript/src/useOnGetState.d.ts +1 -1
  177. package/lib/typescript/src/useOnGetState.d.ts.map +1 -1
  178. package/lib/typescript/src/useOnPreventRemove.d.ts +3 -3
  179. package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
  180. package/lib/typescript/src/useOnRouteFocus.d.ts +6 -6
  181. package/lib/typescript/src/useOnRouteFocus.d.ts.map +1 -1
  182. package/lib/typescript/src/useOptionsGetters.d.ts +1 -1
  183. package/lib/typescript/src/useOptionsGetters.d.ts.map +1 -1
  184. package/lib/typescript/src/usePreventRemoveContext.d.ts +1 -1
  185. package/lib/typescript/src/useRoute.d.ts +2 -2
  186. package/lib/typescript/src/useRoute.d.ts.map +1 -1
  187. package/lib/typescript/src/useRouteCache.d.ts +4 -4
  188. package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
  189. package/lib/typescript/src/useScheduleUpdate.d.ts.map +1 -1
  190. package/lib/typescript/src/useStateForPath.d.ts +1 -1
  191. package/lib/typescript/src/useStateForPath.d.ts.map +1 -1
  192. package/lib/typescript/src/utilities.d.ts +45 -5
  193. package/lib/typescript/src/utilities.d.ts.map +1 -1
  194. package/lib/typescript/src/validatePathConfig.d.ts.map +1 -1
  195. package/package.json +23 -20
  196. package/src/BaseNavigationContainer.tsx +337 -326
  197. package/src/ConsumedParamsContext.tsx +10 -0
  198. package/src/NavigationBuilderContext.tsx +14 -8
  199. package/src/NavigationFocusedRouteStateContext.tsx +4 -4
  200. package/src/NavigationIndependentTree.tsx +8 -5
  201. package/src/NavigationProvider.tsx +17 -3
  202. package/src/NavigationStateContext.tsx +5 -6
  203. package/src/PreventRemoveProvider.tsx +3 -3
  204. package/src/SceneView.tsx +58 -56
  205. package/src/StaticNavigation.tsx +488 -250
  206. package/src/checkSerializable.tsx +22 -12
  207. package/src/createNavigatorFactory.tsx +83 -8
  208. package/src/findFocusedRoute.tsx +13 -8
  209. package/src/getActionFromState.tsx +68 -39
  210. package/src/getPathFromState.tsx +91 -27
  211. package/src/getPatternParts.tsx +8 -0
  212. package/src/getStateFromPath.tsx +555 -180
  213. package/src/getStateFromRouteParams.tsx +60 -0
  214. package/src/index.tsx +47 -5
  215. package/src/theming/useTheme.tsx +1 -1
  216. package/src/types.tsx +541 -338
  217. package/src/useDescriptors.tsx +27 -12
  218. package/src/useEventEmitter.tsx +45 -36
  219. package/src/useFocusEvents.tsx +8 -2
  220. package/src/useFocusedListenersChildrenAdapter.tsx +2 -2
  221. package/src/useIsFocused.tsx +14 -21
  222. package/src/useNavigation.tsx +8 -4
  223. package/src/useNavigationBuilder.tsx +220 -132
  224. package/src/useNavigationCache.tsx +6 -6
  225. package/src/useNavigationHelpers.tsx +1 -1
  226. package/src/useNavigationIndependentTree.tsx +1 -1
  227. package/src/useNavigationState.tsx +11 -7
  228. package/src/useOnAction.tsx +10 -10
  229. package/src/useOnGetState.tsx +5 -3
  230. package/src/useOnPreventRemove.tsx +3 -3
  231. package/src/useOnRouteFocus.tsx +10 -12
  232. package/src/useOptionsGetters.tsx +7 -4
  233. package/src/usePreventRemove.tsx +1 -1
  234. package/src/usePreventRemoveContext.tsx +1 -1
  235. package/src/useRegisterNavigator.tsx +2 -2
  236. package/src/useRoute.tsx +4 -2
  237. package/src/useRouteCache.tsx +2 -1
  238. package/src/useScheduleUpdate.tsx +1 -3
  239. package/src/useStateForPath.tsx +1 -1
  240. package/src/utilities.tsx +101 -5
  241. package/src/validatePathConfig.tsx +1 -0
  242. package/lib/module/CurrentRenderContext.js +0 -10
  243. package/lib/module/CurrentRenderContext.js.map +0 -1
  244. package/lib/module/useCurrentRender.js +0 -19
  245. package/lib/module/useCurrentRender.js.map +0 -1
  246. package/lib/typescript/src/CurrentRenderContext.d.ts +0 -9
  247. package/lib/typescript/src/CurrentRenderContext.d.ts.map +0 -1
  248. package/lib/typescript/src/useCurrentRender.d.ts +0 -14
  249. package/lib/typescript/src/useCurrentRender.d.ts.map +0 -1
  250. package/src/CurrentRenderContext.tsx +0 -9
  251. package/src/useCurrentRender.tsx +0 -31
@@ -15,10 +15,12 @@ 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';
23
25
  import { NavigationMetaContext } from './NavigationMetaContext';
24
26
  import { NavigationRouteContext } from './NavigationProvider';
@@ -36,11 +38,11 @@ import {
36
38
  import { UnhandledActionContext } from './UnhandledActionContext';
37
39
  import { useChildListeners } from './useChildListeners';
38
40
  import { useComponent } from './useComponent';
39
- import { useCurrentRender } from './useCurrentRender';
40
41
  import { type ScreenConfigWithParent, useDescriptors } from './useDescriptors';
41
42
  import { useEventEmitter } from './useEventEmitter';
42
43
  import { useFocusedListenersChildrenAdapter } from './useFocusedListenersChildrenAdapter';
43
44
  import { useFocusEvents } from './useFocusEvents';
45
+ import { FocusedRouteKeyContext } from './useIsFocused';
44
46
  import { useKeyedChildListeners } from './useKeyedChildListeners';
45
47
  import { useLazyValue } from './useLazyValue';
46
48
  import { useNavigationHelpers } from './useNavigationHelpers';
@@ -57,9 +59,17 @@ PrivateValueStore;
57
59
 
58
60
  type NavigatorRoute = {
59
61
  key: string;
60
- params?: NavigatorScreenParams<ParamListBase>;
62
+ params?: NavigatorScreenParams<ParamListBase> | undefined;
61
63
  };
62
64
 
65
+ const isNavigationState = (
66
+ state: unknown
67
+ ): state is NavigationState | PartialState<NavigationState> =>
68
+ state != null &&
69
+ typeof state === 'object' &&
70
+ 'routes' in state &&
71
+ Array.isArray(state.routes);
72
+
63
73
  const isScreen = (
64
74
  child: React.ReactElement<unknown>
65
75
  ): child is React.ReactElement<{
@@ -276,8 +286,10 @@ const getRouteConfigsFromChildren = <
276
286
  };
277
287
 
278
288
  const getStateFromParams = (params: NavigatorRoute['params']) => {
279
- if (params?.state != null) {
280
- return params.state;
289
+ const state = params?.state;
290
+
291
+ if (isNavigationState(state)) {
292
+ return state;
281
293
  } else if (typeof params?.screen === 'string' && params?.initial !== false) {
282
294
  return {
283
295
  routes: [
@@ -319,9 +331,14 @@ export function useNavigationBuilder<
319
331
  ) {
320
332
  const navigatorKey = useRegisterNavigator();
321
333
 
322
- const route = React.useContext(NavigationRouteContext) as
323
- | NavigatorRoute
324
- | undefined;
334
+ const route = React.use(NavigationRouteContext) as NavigatorRoute | undefined;
335
+
336
+ const consumedParams = React.use(ConsumedParamsContext);
337
+
338
+ const isNestedParamsConsumed =
339
+ typeof route?.params === 'object' && route.params != null
340
+ ? consumedParams?.isConsumed(route.params)
341
+ : false;
325
342
 
326
343
  const {
327
344
  children,
@@ -365,44 +382,7 @@ export function useNavigationBuilder<
365
382
  return original;
366
383
  });
367
384
 
368
- const screens = routeConfigs.reduce<
369
- Record<string, ScreenConfigWithParent<State, ScreenOptions, EventMap>>
370
- >((acc, config) => {
371
- if (config.props.name in acc) {
372
- throw new Error(
373
- `A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${config.props.name}')`
374
- );
375
- }
376
-
377
- acc[config.props.name] = config;
378
- return acc;
379
- }, {});
380
-
381
385
  const routeNames = routeConfigs.map((config) => config.props.name);
382
- const routeKeyList = routeNames.reduce<Record<string, React.Key | undefined>>(
383
- (acc, curr) => {
384
- acc[curr] = screens[curr].keys.map((key) => key ?? '').join(':');
385
- return acc;
386
- },
387
- {}
388
- );
389
- const routeParamList = routeNames.reduce<Record<string, object | undefined>>(
390
- (acc, curr) => {
391
- const { initialParams } = screens[curr].props;
392
- acc[curr] = initialParams;
393
- return acc;
394
- },
395
- {}
396
- );
397
- const routeGetIdList = routeNames.reduce<
398
- RouterConfigOptions['routeGetIdList']
399
- >(
400
- (acc, curr) =>
401
- Object.assign(acc, {
402
- [curr]: screens[curr].props.getId,
403
- }),
404
- {}
405
- );
406
386
 
407
387
  if (!routeNames.length) {
408
388
  throw new Error(
@@ -410,6 +390,31 @@ export function useNavigationBuilder<
410
390
  );
411
391
  }
412
392
 
393
+ const screens: Record<
394
+ string,
395
+ ScreenConfigWithParent<State, ScreenOptions, EventMap>
396
+ > = {};
397
+
398
+ const routeKeyList: Record<string, React.Key | undefined> = {};
399
+ const routeParamList: Record<string, object | undefined> = {};
400
+ const routeGetIdList: RouterConfigOptions['routeGetIdList'] = {};
401
+
402
+ for (const config of routeConfigs) {
403
+ const name = config.props.name;
404
+
405
+ if (name in screens) {
406
+ throw new Error(
407
+ `A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${name}')`
408
+ );
409
+ }
410
+
411
+ screens[name] = config;
412
+ routeKeyList[name] = config.keys.map((key) => key ?? '').join(':');
413
+ routeParamList[name] = config.props.initialParams;
414
+
415
+ Object.assign(routeGetIdList, { [name]: config.props.getId });
416
+ }
417
+
413
418
  const isStateValid = React.useCallback(
414
419
  (state: NavigationState | PartialState<NavigationState>) =>
415
420
  state.type === undefined || state.type === router.type,
@@ -417,7 +422,9 @@ export function useNavigationBuilder<
417
422
  );
418
423
 
419
424
  const isStateInitialized = React.useCallback(
420
- (state: NavigationState | PartialState<NavigationState> | undefined) =>
425
+ <T extends NavigationState>(
426
+ state: T | PartialState<T> | undefined
427
+ ): state is T =>
421
428
  state !== undefined && state.stale === false && isStateValid(state),
422
429
  [isStateValid]
423
430
  );
@@ -435,16 +442,22 @@ export function useNavigationBuilder<
435
442
  setKey,
436
443
  getKey,
437
444
  getIsInitial,
438
- } = React.useContext(NavigationStateContext);
445
+ } = React.use(NavigationStateContext);
439
446
 
440
- const stateCleanedUp = React.useRef(false);
447
+ const stateCleanupRef = React.useRef<boolean>(false);
448
+ const lastStateRef = React.useRef<State | PartialState<State> | undefined>(
449
+ undefined
450
+ );
441
451
 
442
452
  const setState = useLatestCallback(
443
- (state: NavigationState | PartialState<NavigationState> | undefined) => {
444
- if (stateCleanedUp.current) {
453
+ (state: State | PartialState<State> | undefined) => {
454
+ if (stateCleanupRef.current) {
455
+ // Store the state locally in case the current navigator is in `Activity`
456
+ lastStateRef.current = state;
457
+
445
458
  // State might have been already cleaned up due to unmount
446
- // We do not want to expose API allowing to override this
447
- // This would lead to old data preservation on main navigator unmount
459
+ // We don't want to update `route.state` in parent
460
+ // Otherwise it will be reused if a new navigator gets mounted
448
461
  return;
449
462
  }
450
463
 
@@ -456,15 +469,36 @@ export function useNavigationBuilder<
456
469
  stateBeforeInitialization,
457
470
  initializedState,
458
471
  isFirstStateInitialization,
472
+ paramsUsedForInitialization,
459
473
  ] = React.useMemo((): [
460
474
  PartialState<State> | undefined,
461
475
  State | undefined,
462
476
  boolean,
477
+ object | undefined,
463
478
  ] => {
479
+ // If the state was already cleaned up, but we have it stored in ref,
480
+ // It likely got cleaned up due to `<Activity mode="hidden">`
481
+ // We should reuse this state to avoid remounting screens
482
+ if (
483
+ stateCleanupRef.current &&
484
+ lastStateRef.current &&
485
+ isStateValid(lastStateRef.current)
486
+ ) {
487
+ const state: State = isStateInitialized(lastStateRef.current)
488
+ ? lastStateRef.current
489
+ : router.getRehydratedState(lastStateRef.current, {
490
+ routeNames,
491
+ routeParamList,
492
+ routeGetIdList,
493
+ });
494
+
495
+ return [undefined, state, false, undefined];
496
+ }
497
+
464
498
  const initialRouteParamList = routeNames.reduce<
465
499
  Record<string, object | undefined>
466
500
  >((acc, curr) => {
467
- const { initialParams } = screens[curr].props;
501
+ const { initialParams } = screens[curr]?.props ?? {};
468
502
  const initialParamsFromParams =
469
503
  route?.params?.state == null &&
470
504
  route?.params?.initial !== false &&
@@ -493,7 +527,8 @@ export function useNavigationBuilder<
493
527
  !(
494
528
  typeof route?.params?.screen === 'string' &&
495
529
  route?.params?.initial !== false
496
- )
530
+ ) &&
531
+ !isNestedParamsConsumed
497
532
  ) {
498
533
  return [
499
534
  undefined,
@@ -503,28 +538,40 @@ export function useNavigationBuilder<
503
538
  routeGetIdList,
504
539
  }),
505
540
  true,
541
+ undefined,
506
542
  ];
507
543
  } else {
508
- const stateFromParams = getStateFromParams(route?.params);
509
- const stateBeforeInitialization = (stateFromParams ??
510
- currentState) as PartialState<State>;
511
- const hydratedState = router.getRehydratedState(
512
- stateBeforeInitialization,
513
- {
514
- routeNames,
515
- routeParamList: initialRouteParamList,
516
- routeGetIdList,
517
- }
518
- );
544
+ const paramsForState = isNestedParamsConsumed ? undefined : route?.params;
545
+ const stateFromParams = paramsForState
546
+ ? getStateFromParams(paramsForState)
547
+ : undefined;
548
+
549
+ const stateBeforeInitialization = (stateFromParams ?? currentState) as
550
+ | PartialState<State>
551
+ | undefined;
552
+
553
+ const hydratedState =
554
+ stateBeforeInitialization == null
555
+ ? router.getInitialState({
556
+ routeNames,
557
+ routeParamList: initialRouteParamList,
558
+ routeGetIdList,
559
+ })
560
+ : router.getRehydratedState(stateBeforeInitialization, {
561
+ routeNames,
562
+ routeParamList: initialRouteParamList,
563
+ routeGetIdList,
564
+ });
519
565
 
520
566
  if (
567
+ stateBeforeInitialization != null &&
521
568
  options.routeNamesChangeBehavior === 'lastUnhandled' &&
522
569
  doesStateHaveOnlyInvalidRoutes(stateBeforeInitialization)
523
570
  ) {
524
- return [stateBeforeInitialization, hydratedState, true];
571
+ return [stateBeforeInitialization, hydratedState, true, paramsForState];
525
572
  }
526
573
 
527
- return [undefined, hydratedState, false];
574
+ return [undefined, hydratedState, false, paramsForState];
528
575
  }
529
576
  // We explicitly don't include routeNames, route.params etc. in the dep list
530
577
  // below. We want to avoid forcing a new state to be calculated in those cases
@@ -602,38 +649,33 @@ export function useNavigationBuilder<
602
649
  });
603
650
  }
604
651
 
605
- const previousNestedParamsRef = React.useRef(route?.params);
606
-
607
- React.useEffect(() => {
608
- previousNestedParamsRef.current = route?.params;
609
- }, [route?.params]);
610
-
611
- if (route?.params) {
612
- const previousParams = previousNestedParamsRef.current;
652
+ let didConsumeNestedParams = route?.params === paramsUsedForInitialization;
613
653
 
654
+ if (route?.params && !didConsumeNestedParams) {
614
655
  let action: CommonActions.Action | undefined;
656
+ const stateFromParams = route.params.state;
657
+
658
+ if (isNavigationState(stateFromParams) && !isNestedParamsConsumed) {
659
+ didConsumeNestedParams = true;
615
660
 
616
- if (
617
- typeof route.params.state === 'object' &&
618
- route.params.state != null &&
619
- route.params !== previousParams
620
- ) {
621
661
  if (
622
662
  options.routeNamesChangeBehavior === 'lastUnhandled' &&
623
- doesStateHaveOnlyInvalidRoutes(route.params.state)
663
+ doesStateHaveOnlyInvalidRoutes(stateFromParams)
624
664
  ) {
625
- if (route.params.state !== unhandledState) {
626
- setUnhandledState(route.params.state);
665
+ if (stateFromParams !== unhandledState) {
666
+ setUnhandledState(stateFromParams);
627
667
  }
628
668
  } else {
629
669
  // If the route was updated with new state, we should reset to it
630
- action = CommonActions.reset(route.params.state);
670
+ action = CommonActions.reset(stateFromParams);
631
671
  }
632
672
  } else if (
633
673
  typeof route.params.screen === 'string' &&
634
674
  ((route.params.initial === false && isFirstStateInitialization) ||
635
- route.params !== previousParams)
675
+ !isNestedParamsConsumed)
636
676
  ) {
677
+ didConsumeNestedParams = true;
678
+
637
679
  if (
638
680
  options.routeNamesChangeBehavior === 'lastUnhandled' &&
639
681
  !routeNames.includes(route.params.screen)
@@ -674,14 +716,24 @@ export function useNavigationBuilder<
674
716
  : nextState;
675
717
  }
676
718
 
677
- const shouldUpdate =
678
- state !== nextState ||
679
- typeof route?.params?.state === 'object' ||
680
- typeof route?.params?.screen === 'string';
719
+ const setConsumedParams = consumedParams?.setConsumed;
720
+
721
+ React.useEffect(() => {
722
+ if (
723
+ setConsumedParams &&
724
+ didConsumeNestedParams &&
725
+ typeof route?.params === 'object' &&
726
+ route.params != null
727
+ ) {
728
+ setConsumedParams(route.params);
729
+ }
730
+ }, [didConsumeNestedParams, route?.params, setConsumedParams]);
731
+
732
+ const shouldUpdate = state !== nextState;
681
733
 
682
734
  useScheduleUpdate(() => {
683
735
  if (shouldUpdate) {
684
- // If the state needs to be updated, we'll schedule an update
736
+ // Schedule an update if the state needs to be updated
685
737
  setState(nextState);
686
738
 
687
739
  if (shouldClearUnhandledState) {
@@ -695,26 +747,41 @@ export function useNavigationBuilder<
695
747
  // So we override the state object we return to use the latest state as soon as possible
696
748
  state = nextState;
697
749
 
750
+ // Last state to reuse if component gets cleaned up due to `<Activity mode="hidden">`
751
+ React.useEffect(() => {
752
+ lastStateRef.current = state;
753
+ });
754
+
755
+ const lastNotifiedStateRef = React.useRef<State | null>(null);
756
+
698
757
  React.useEffect(() => {
699
758
  // In strict mode, React will double-invoke effects.
700
759
  // So we need to reset the flag if component was not unmounted
701
- stateCleanedUp.current = false;
760
+ stateCleanupRef.current = false;
702
761
 
703
762
  setKey(navigatorKey);
704
763
 
705
- if (!getIsInitial()) {
764
+ if (!getIsInitial() && lastNotifiedStateRef.current !== state) {
706
765
  // If it's not initial render, we need to update the state
707
766
  // This will make sure that our container gets notifier of state changes due to new mounts
708
767
  // This is necessary for proper screen tracking, URL updates etc.
709
- setState(nextState);
768
+ // We only notify if the state is different what we already notified
769
+ // Otherwise this goes into a loop when inside `<Activity mode="hidden">`
770
+ setState(state);
771
+ lastNotifiedStateRef.current = state;
710
772
  }
711
773
 
712
774
  return () => {
713
775
  // We need to clean up state for this navigator on unmount
714
776
  if (getCurrentState() !== undefined && getKey() === navigatorKey) {
715
777
  setCurrentState(undefined);
716
- stateCleanedUp.current = true;
778
+ stateCleanupRef.current = true;
717
779
  }
780
+
781
+ // Reset so that StrictMode's second mount re-propagates state correctly.
782
+ // Without this, the guard above sees the same reference and skips setState,
783
+ // causing the initial state to be lost after cleanup wipes the container state.
784
+ lastNotifiedStateRef.current = null;
718
785
  };
719
786
  // eslint-disable-next-line react-hooks/exhaustive-deps
720
787
  }, []);
@@ -742,6 +809,8 @@ export function useNavigationBuilder<
742
809
  );
743
810
  });
744
811
 
812
+ const { onEmitEvent } = React.use(NavigationBuilderContext);
813
+
745
814
  const emitter = useEventEmitter<EventMapCore<State>>((e) => {
746
815
  const routeNames = [];
747
816
 
@@ -764,35 +833,54 @@ export function useNavigationBuilder<
764
833
  return;
765
834
  }
766
835
 
767
- const navigation = descriptors[route.key].navigation;
768
-
769
- const listeners = ([] as (((e: any) => void) | undefined)[])
770
- .concat(
771
- // Get an array of listeners for all screens + common listeners on navigator
772
- ...[
773
- screenListeners,
774
- ...routeNames.map((name) => {
775
- const { listeners } = screens[name].props;
776
- return listeners;
777
- }),
778
- ].map((listeners) => {
779
- const map =
780
- typeof listeners === 'function'
781
- ? listeners({ route: route as any, navigation })
782
- : listeners;
783
-
784
- return map
785
- ? Object.keys(map)
786
- .filter((type) => type === e.type)
787
- .map((type) => map?.[type])
788
- : undefined;
789
- })
790
- )
791
- // We don't want same listener to be called multiple times for same event
792
- // So we remove any duplicate functions from the array
793
- .filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
836
+ const hasPerScreenListeners = routeNames.some(
837
+ (name) => screens[name]?.props.listeners != null
838
+ );
839
+
840
+ if (screenListeners != null || hasPerScreenListeners) {
841
+ const descriptor = descriptors[route.key];
842
+
843
+ if (descriptor == null) {
844
+ throw new Error(`Couldn't find a descriptor for route '${route.key}'.`);
845
+ }
846
+
847
+ const navigation = descriptor.navigation;
848
+
849
+ const listeners = ([] as (((e: any) => void) | undefined)[])
850
+ .concat(
851
+ // Get an array of listeners for all screens + common listeners on navigator
852
+ ...[
853
+ screenListeners,
854
+ ...routeNames.map((name) => {
855
+ const { listeners } = screens[name]?.props ?? {};
856
+ return listeners;
857
+ }),
858
+ ].map((listeners) => {
859
+ const map =
860
+ typeof listeners === 'function'
861
+ ? listeners({ route: route as any, navigation })
862
+ : listeners;
863
+
864
+ return map
865
+ ? Object.keys(map)
866
+ .filter((type) => type === e.type)
867
+ .map((type) => map?.[type])
868
+ : undefined;
869
+ })
870
+ )
871
+ // We don't want same listener to be called multiple times for same event
872
+ // So we remove any duplicate functions from the array
873
+ .filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
874
+
875
+ listeners.forEach((listener) => listener?.(e));
876
+ }
794
877
 
795
- listeners.forEach((listener) => listener?.(e));
878
+ onEmitEvent({
879
+ type: e.type,
880
+ data: e.data,
881
+ target: e.target,
882
+ defaultPrevented: e.defaultPrevented,
883
+ });
796
884
  });
797
885
 
798
886
  useFocusEvents({ state, emitter });
@@ -827,7 +915,7 @@ export function useNavigationBuilder<
827
915
  setState,
828
916
  });
829
917
 
830
- const onUnhandledActionParent = React.useContext(UnhandledActionContext);
918
+ const onUnhandledActionParent = React.use(UnhandledActionContext);
831
919
 
832
920
  const onUnhandledAction = useLatestCallback((action: NavigationAction) => {
833
921
  if (
@@ -893,9 +981,7 @@ export function useNavigationBuilder<
893
981
  ScreenOptions,
894
982
  EventMap
895
983
  >({
896
- routes: router.getRoutesFromState
897
- ? router.getRoutesFromState(state)
898
- : state.routes,
984
+ routes: state.routes,
899
985
  screens,
900
986
  navigation,
901
987
  screenOptions,
@@ -911,13 +997,13 @@ export function useNavigationBuilder<
911
997
  emitter,
912
998
  });
913
999
 
914
- useCurrentRender({
915
- state,
916
- navigation,
917
- descriptors,
918
- });
919
-
920
1000
  const NavigationContent = useComponent((children: React.ReactNode) => {
1001
+ const focusedRoute = state.routes[state.index];
1002
+
1003
+ if (focusedRoute == null) {
1004
+ throw new Error(`Couldn't find a route at index ${state.index}.`);
1005
+ }
1006
+
921
1007
  const element =
922
1008
  layout != null
923
1009
  ? layout({
@@ -932,7 +1018,9 @@ export function useNavigationBuilder<
932
1018
  <NavigationMetaContext.Provider value={undefined}>
933
1019
  <NavigationHelpersContext.Provider value={navigation}>
934
1020
  <NavigationStateListenerProvider state={state}>
935
- <PreventRemoveProvider>{element}</PreventRemoveProvider>
1021
+ <FocusedRouteKeyContext.Provider value={focusedRoute.key}>
1022
+ <PreventRemoveProvider>{element}</PreventRemoveProvider>
1023
+ </FocusedRouteKeyContext.Provider>
936
1024
  </NavigationStateListenerProvider>
937
1025
  </NavigationHelpersContext.Provider>
938
1026
  </NavigationMetaContext.Provider>
@@ -23,8 +23,8 @@ type Options<
23
23
  Partial<NavigationProp<ParamListBase, string, any, any, any>>;
24
24
  setOptions: (
25
25
  cb: (
26
- options: Record<string, ScreenOptions>
27
- ) => Record<string, ScreenOptions>
26
+ options: Record<string, Partial<ScreenOptions> | undefined>
27
+ ) => Record<string, Partial<ScreenOptions> | undefined>
28
28
  ) => void;
29
29
  router: Router<State, NavigationAction>;
30
30
  emitter: NavigationEventEmitter<EventMap>;
@@ -72,8 +72,8 @@ export function useNavigationCache<
72
72
  router,
73
73
  emitter,
74
74
  }: Options<State, ScreenOptions, EventMap>) {
75
- const parentNavigationHelpers = React.useContext(NavigationContext);
76
- const { stackRef } = React.useContext(NavigationBuilderContext);
75
+ const parentNavigationHelpers = React.use(NavigationContext);
76
+ const { stackRef } = React.use(NavigationBuilderContext);
77
77
 
78
78
  // Cache object which holds navigation objects for each screen
79
79
  // We use `React.useMemo` instead of `React.useRef` coz we want to invalidate it when deps change
@@ -174,7 +174,7 @@ export function useNavigationCache<
174
174
 
175
175
  return parentNavigationHelpers;
176
176
  },
177
- setOptions: (options: object) => {
177
+ setOptions: (options: Partial<ScreenOptions>) => {
178
178
  setOptions((o) => ({
179
179
  ...o,
180
180
  [route.key]: { ...o[route.key], ...options },
@@ -183,7 +183,7 @@ export function useNavigationCache<
183
183
  isFocused: () => {
184
184
  const state = rest.getState();
185
185
 
186
- if (state.routes[state.index].key !== route.key) {
186
+ if (state.routes[state.index]?.key !== route.key) {
187
187
  return false;
188
188
  }
189
189
 
@@ -41,7 +41,7 @@ export function useNavigationHelpers<
41
41
  router,
42
42
  stateRef,
43
43
  }: Options<State, Action>) {
44
- const parentNavigationHelpers = React.useContext(NavigationContext);
44
+ const parentNavigationHelpers = React.use(NavigationContext);
45
45
 
46
46
  return React.useMemo(() => {
47
47
  const dispatch = (op: Action | ((state: State) => Action)) => {
@@ -3,5 +3,5 @@ import * as React from 'react';
3
3
  import { NavigationIndependentTreeContext } from './NavigationIndependentTreeContext';
4
4
 
5
5
  export function useNavigationIndependentTree() {
6
- return React.useContext(NavigationIndependentTreeContext);
6
+ return React.use(NavigationIndependentTreeContext);
7
7
  }
@@ -15,6 +15,8 @@ type NavigationStateListener = {
15
15
  subscribe: (callback: () => void) => () => void;
16
16
  };
17
17
 
18
+ type UseNavigationForName = (name: string) => NavigationProp<ParamListBase>;
19
+
18
20
  type NavigationStateForNested<
19
21
  Navigator,
20
22
  RouteName extends keyof NavigationListForNested<Navigator>,
@@ -35,8 +37,8 @@ type NavigationStateForNested<
35
37
  export function useNavigationState<
36
38
  const T,
37
39
  const Navigator = RootNavigator,
38
- const RouteName extends
39
- keyof NavigationListForNested<Navigator> = keyof NavigationListForNested<Navigator>,
40
+ const RouteName extends keyof NavigationListForNested<Navigator> =
41
+ keyof NavigationListForNested<Navigator>,
40
42
  >(
41
43
  routeName: RouteName,
42
44
  selector: (state: NavigationStateForNested<Navigator, RouteName>) => T
@@ -51,9 +53,9 @@ export function useNavigationState(...args: unknown[]): unknown {
51
53
 
52
54
  if (typeof args[0] === 'string') {
53
55
  // `useNavigation` uses `use` internally, so it's fine to call it conditionally
54
- // @ts-expect-error we can't specify the type here
55
- // eslint-disable-next-line react-hooks/rules-of-hooks
56
- navigation = useNavigation(args[0]);
56
+ // Cast to a non-generic signature to skip overload resolution - otherwise TS
57
+ // eagerly expands `NavigationListForNested<RootNavigator>` at this call site.
58
+ navigation = (useNavigation as UseNavigationForName)(args[0]);
57
59
  selector = args[1];
58
60
  } else {
59
61
  selector = args[0];
@@ -117,8 +119,9 @@ export function NavigationStateListenerProvider({
117
119
  children: React.ReactNode;
118
120
  }) {
119
121
  const listeners = React.useRef<(() => void)[]>([]);
122
+ const stateRef = React.useRef(state);
120
123
 
121
- const getState = useLatestCallback(() => state);
124
+ const getState = useLatestCallback(() => stateRef.current);
122
125
 
123
126
  const subscribe = useLatestCallback((callback: () => void) => {
124
127
  listeners.current.push(callback);
@@ -128,7 +131,8 @@ export function NavigationStateListenerProvider({
128
131
  };
129
132
  });
130
133
 
131
- React.useEffect(() => {
134
+ React.useLayoutEffect(() => {
135
+ stateRef.current = state;
132
136
  listeners.current.forEach((callback) => callback());
133
137
  }, [state]);
134
138