@react-navigation/core 8.0.0-alpha.3 → 8.0.0-alpha.31

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 (285) hide show
  1. package/lib/module/BaseNavigationContainer.js +38 -19
  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/DataLoading.js +127 -0
  6. package/lib/module/DataLoading.js.map +1 -0
  7. package/lib/module/NavigationBuilderContext.js +1 -0
  8. package/lib/module/NavigationBuilderContext.js.map +1 -1
  9. package/lib/module/NavigationIndependentTree.js +20 -8
  10. package/lib/module/NavigationIndependentTree.js.map +1 -1
  11. package/lib/module/NavigationProvider.js +22 -11
  12. package/lib/module/NavigationProvider.js.map +1 -1
  13. package/lib/module/NavigationStateContext.js +3 -0
  14. package/lib/module/NavigationStateContext.js.map +1 -1
  15. package/lib/module/PreventRemoveContext.js.map +1 -1
  16. package/lib/module/PreventRemoveProvider.js +37 -27
  17. package/lib/module/PreventRemoveProvider.js.map +1 -1
  18. package/lib/module/SceneView.js +10 -41
  19. package/lib/module/SceneView.js.map +1 -1
  20. package/lib/module/StaticNavigation.js +179 -50
  21. package/lib/module/StaticNavigation.js.map +1 -1
  22. package/lib/module/StaticTreeContext.js +5 -0
  23. package/lib/module/StaticTreeContext.js.map +1 -0
  24. package/lib/module/checkSerializable.js +18 -13
  25. package/lib/module/checkSerializable.js.map +1 -1
  26. package/lib/module/createNavigationContainerRef.js +6 -8
  27. package/lib/module/createNavigationContainerRef.js.map +1 -1
  28. package/lib/module/createNavigatorFactory.js +27 -5
  29. package/lib/module/createNavigatorFactory.js.map +1 -1
  30. package/lib/module/findFocusedRoute.js +7 -4
  31. package/lib/module/findFocusedRoute.js.map +1 -1
  32. package/lib/module/getActionFromState.js +45 -20
  33. package/lib/module/getActionFromState.js.map +1 -1
  34. package/lib/module/getPathFromState.js +171 -90
  35. package/lib/module/getPathFromState.js.map +1 -1
  36. package/lib/module/getPatternParts.js +20 -4
  37. package/lib/module/getPatternParts.js.map +1 -1
  38. package/lib/module/getStateFromPath.js +487 -217
  39. package/lib/module/getStateFromPath.js.map +1 -1
  40. package/lib/module/getStateFromRouteParams.js +23 -0
  41. package/lib/module/getStateFromRouteParams.js.map +1 -0
  42. package/lib/module/index.js +5 -4
  43. package/lib/module/index.js.map +1 -1
  44. package/lib/module/theming/useTheme.js +1 -1
  45. package/lib/module/theming/useTheme.js.map +1 -1
  46. package/lib/module/types.js +35 -0
  47. package/lib/module/types.js.map +1 -1
  48. package/lib/module/useDescriptors.js +21 -11
  49. package/lib/module/useDescriptors.js.map +1 -1
  50. package/lib/module/useEventEmitter.js +48 -33
  51. package/lib/module/useEventEmitter.js.map +1 -1
  52. package/lib/module/useFocusEvents.js +12 -4
  53. package/lib/module/useFocusEvents.js.map +1 -1
  54. package/lib/module/useFocusedListenersChildrenAdapter.js +2 -2
  55. package/lib/module/useFocusedListenersChildrenAdapter.js.map +1 -1
  56. package/lib/module/useIsFocused.js +7 -12
  57. package/lib/module/useIsFocused.js.map +1 -1
  58. package/lib/module/useKeyedChildListeners.js +4 -2
  59. package/lib/module/useKeyedChildListeners.js.map +1 -1
  60. package/lib/module/useLazyValue.js.map +1 -1
  61. package/lib/module/useNavigation.js +2 -15
  62. package/lib/module/useNavigation.js.map +1 -1
  63. package/lib/module/useNavigationBuilder.js +204 -131
  64. package/lib/module/useNavigationBuilder.js.map +1 -1
  65. package/lib/module/useNavigationCache.js +22 -17
  66. package/lib/module/useNavigationCache.js.map +1 -1
  67. package/lib/module/useNavigationHelpers.js +28 -10
  68. package/lib/module/useNavigationHelpers.js.map +1 -1
  69. package/lib/module/useNavigationIndependentTree.js +1 -1
  70. package/lib/module/useNavigationIndependentTree.js.map +1 -1
  71. package/lib/module/useNavigationState.js +125 -45
  72. package/lib/module/useNavigationState.js.map +1 -1
  73. package/lib/module/useOnAction.js +37 -7
  74. package/lib/module/useOnAction.js.map +1 -1
  75. package/lib/module/useOnGetState.js +6 -3
  76. package/lib/module/useOnGetState.js.map +1 -1
  77. package/lib/module/useOnPreventRemove.js +39 -30
  78. package/lib/module/useOnPreventRemove.js.map +1 -1
  79. package/lib/module/useOnRouteFocus.js +1 -1
  80. package/lib/module/useOnRouteFocus.js.map +1 -1
  81. package/lib/module/useOptionsGetters.js +10 -5
  82. package/lib/module/useOptionsGetters.js.map +1 -1
  83. package/lib/module/usePreventRemove.js +10 -3
  84. package/lib/module/usePreventRemove.js.map +1 -1
  85. package/lib/module/usePreventRemoveContext.js +1 -1
  86. package/lib/module/usePreventRemoveContext.js.map +1 -1
  87. package/lib/module/useRegisterNavigator.js +2 -2
  88. package/lib/module/useRegisterNavigator.js.map +1 -1
  89. package/lib/module/useRoute.js +2 -0
  90. package/lib/module/useRoute.js.map +1 -1
  91. package/lib/module/useRouteCache.js +17 -11
  92. package/lib/module/useRouteCache.js.map +1 -1
  93. package/lib/module/useScheduleUpdate.js +9 -9
  94. package/lib/module/useScheduleUpdate.js.map +1 -1
  95. package/lib/module/useStateForPath.js +1 -1
  96. package/lib/module/useStateForPath.js.map +1 -1
  97. package/lib/module/useSyncState.js +32 -9
  98. package/lib/module/useSyncState.js.map +1 -1
  99. package/lib/module/validatePathConfig.js +50 -36
  100. package/lib/module/validatePathConfig.js.map +1 -1
  101. package/lib/typescript/src/BaseNavigationContainer.d.ts +6 -3
  102. package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
  103. package/lib/typescript/src/ConsumedParamsContext.d.ts +3 -0
  104. package/lib/typescript/src/ConsumedParamsContext.d.ts.map +1 -0
  105. package/lib/typescript/src/DataLoading.d.ts +21 -0
  106. package/lib/typescript/src/DataLoading.d.ts.map +1 -0
  107. package/lib/typescript/src/EnsureSingleNavigator.d.ts +1 -1
  108. package/lib/typescript/src/EnsureSingleNavigator.d.ts.map +1 -1
  109. package/lib/typescript/src/Group.d.ts +1 -1
  110. package/lib/typescript/src/Group.d.ts.map +1 -1
  111. package/lib/typescript/src/NavigationBuilderContext.d.ts +9 -8
  112. package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
  113. package/lib/typescript/src/NavigationContainerRefContext.d.ts +1 -1
  114. package/lib/typescript/src/NavigationContainerRefContext.d.ts.map +1 -1
  115. package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts +4 -4
  116. package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts.map +1 -1
  117. package/lib/typescript/src/NavigationHelpersContext.d.ts +1 -1
  118. package/lib/typescript/src/NavigationHelpersContext.d.ts.map +1 -1
  119. package/lib/typescript/src/NavigationIndependentTree.d.ts +1 -1
  120. package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
  121. package/lib/typescript/src/NavigationProvider.d.ts +12 -13
  122. package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
  123. package/lib/typescript/src/NavigationStateContext.d.ts +4 -3
  124. package/lib/typescript/src/NavigationStateContext.d.ts.map +1 -1
  125. package/lib/typescript/src/PreventRemoveContext.d.ts +1 -0
  126. package/lib/typescript/src/PreventRemoveContext.d.ts.map +1 -1
  127. package/lib/typescript/src/PreventRemoveProvider.d.ts +1 -1
  128. package/lib/typescript/src/PreventRemoveProvider.d.ts.map +1 -1
  129. package/lib/typescript/src/SceneView.d.ts +4 -2
  130. package/lib/typescript/src/SceneView.d.ts.map +1 -1
  131. package/lib/typescript/src/Screen.d.ts +1 -1
  132. package/lib/typescript/src/Screen.d.ts.map +1 -1
  133. package/lib/typescript/src/StaticContainer.d.ts +1 -1
  134. package/lib/typescript/src/StaticContainer.d.ts.map +1 -1
  135. package/lib/typescript/src/StaticNavigation.d.ts +189 -109
  136. package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
  137. package/lib/typescript/src/StaticTreeContext.d.ts +4 -0
  138. package/lib/typescript/src/StaticTreeContext.d.ts.map +1 -0
  139. package/lib/typescript/src/checkSerializable.d.ts +8 -4
  140. package/lib/typescript/src/checkSerializable.d.ts.map +1 -1
  141. package/lib/typescript/src/createNavigationContainerRef.d.ts +1 -1
  142. package/lib/typescript/src/createNavigationContainerRef.d.ts.map +1 -1
  143. package/lib/typescript/src/createNavigatorFactory.d.ts +23 -5
  144. package/lib/typescript/src/createNavigatorFactory.d.ts.map +1 -1
  145. package/lib/typescript/src/findFocusedRoute.d.ts +3 -8
  146. package/lib/typescript/src/findFocusedRoute.d.ts.map +1 -1
  147. package/lib/typescript/src/getActionFromState.d.ts +4 -4
  148. package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
  149. package/lib/typescript/src/getPathFromState.d.ts +3 -3
  150. package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
  151. package/lib/typescript/src/getPatternParts.d.ts +1 -0
  152. package/lib/typescript/src/getPatternParts.d.ts.map +1 -1
  153. package/lib/typescript/src/getStateFromPath.d.ts +4 -7
  154. package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
  155. package/lib/typescript/src/getStateFromRouteParams.d.ts +3 -0
  156. package/lib/typescript/src/getStateFromRouteParams.d.ts.map +1 -0
  157. package/lib/typescript/src/index.d.ts +36 -33
  158. package/lib/typescript/src/index.d.ts.map +1 -1
  159. package/lib/typescript/src/theming/ThemeContext.d.ts +1 -1
  160. package/lib/typescript/src/theming/ThemeContext.d.ts.map +1 -1
  161. package/lib/typescript/src/theming/ThemeProvider.d.ts +2 -2
  162. package/lib/typescript/src/theming/ThemeProvider.d.ts.map +1 -1
  163. package/lib/typescript/src/theming/useTheme.d.ts +1 -1
  164. package/lib/typescript/src/theming/useTheme.d.ts.map +1 -1
  165. package/lib/typescript/src/types.d.ts +260 -159
  166. package/lib/typescript/src/types.d.ts.map +1 -1
  167. package/lib/typescript/src/useChildListeners.d.ts +3 -3
  168. package/lib/typescript/src/useChildListeners.d.ts.map +1 -1
  169. package/lib/typescript/src/useComponent.d.ts +1 -1
  170. package/lib/typescript/src/useComponent.d.ts.map +1 -1
  171. package/lib/typescript/src/useDescriptors.d.ts +10 -9
  172. package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
  173. package/lib/typescript/src/useEventEmitter.d.ts +2 -2
  174. package/lib/typescript/src/useEventEmitter.d.ts.map +1 -1
  175. package/lib/typescript/src/useFocusEvents.d.ts +2 -2
  176. package/lib/typescript/src/useFocusEvents.d.ts.map +1 -1
  177. package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts +2 -2
  178. package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts.map +1 -1
  179. package/lib/typescript/src/useIsFocused.d.ts +3 -0
  180. package/lib/typescript/src/useIsFocused.d.ts.map +1 -1
  181. package/lib/typescript/src/useKeyedChildListeners.d.ts +3 -3
  182. package/lib/typescript/src/useKeyedChildListeners.d.ts.map +1 -1
  183. package/lib/typescript/src/useLazyValue.d.ts.map +1 -1
  184. package/lib/typescript/src/useNavigation.d.ts +4 -2
  185. package/lib/typescript/src/useNavigation.d.ts.map +1 -1
  186. package/lib/typescript/src/useNavigationBuilder.d.ts +23 -30
  187. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  188. package/lib/typescript/src/useNavigationCache.d.ts +3 -3
  189. package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
  190. package/lib/typescript/src/useNavigationContainerRef.d.ts +1 -1
  191. package/lib/typescript/src/useNavigationContainerRef.d.ts.map +1 -1
  192. package/lib/typescript/src/useNavigationHelpers.d.ts +19 -27
  193. package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
  194. package/lib/typescript/src/useNavigationState.d.ts +17 -3
  195. package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
  196. package/lib/typescript/src/useOnAction.d.ts +9 -9
  197. package/lib/typescript/src/useOnAction.d.ts.map +1 -1
  198. package/lib/typescript/src/useOnGetState.d.ts +1 -1
  199. package/lib/typescript/src/useOnGetState.d.ts.map +1 -1
  200. package/lib/typescript/src/useOnPreventRemove.d.ts +5 -9
  201. package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
  202. package/lib/typescript/src/useOnRouteFocus.d.ts +6 -6
  203. package/lib/typescript/src/useOnRouteFocus.d.ts.map +1 -1
  204. package/lib/typescript/src/useOptionsGetters.d.ts +1 -1
  205. package/lib/typescript/src/useOptionsGetters.d.ts.map +1 -1
  206. package/lib/typescript/src/usePreventRemove.d.ts.map +1 -1
  207. package/lib/typescript/src/usePreventRemoveContext.d.ts +2 -1
  208. package/lib/typescript/src/usePreventRemoveContext.d.ts.map +1 -1
  209. package/lib/typescript/src/useRoute.d.ts +7 -3
  210. package/lib/typescript/src/useRoute.d.ts.map +1 -1
  211. package/lib/typescript/src/useRouteCache.d.ts +5 -5
  212. package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
  213. package/lib/typescript/src/useScheduleUpdate.d.ts +5 -4
  214. package/lib/typescript/src/useScheduleUpdate.d.ts.map +1 -1
  215. package/lib/typescript/src/useStateForPath.d.ts +1 -1
  216. package/lib/typescript/src/useStateForPath.d.ts.map +1 -1
  217. package/lib/typescript/src/useSyncState.d.ts +1 -0
  218. package/lib/typescript/src/useSyncState.d.ts.map +1 -1
  219. package/lib/typescript/src/utilities.d.ts +74 -37
  220. package/lib/typescript/src/utilities.d.ts.map +1 -1
  221. package/lib/typescript/src/validatePathConfig.d.ts.map +1 -1
  222. package/package.json +23 -22
  223. package/src/BaseNavigationContainer.tsx +362 -334
  224. package/src/ConsumedParamsContext.tsx +5 -0
  225. package/src/DataLoading.tsx +203 -0
  226. package/src/NavigationBuilderContext.tsx +14 -9
  227. package/src/NavigationFocusedRouteStateContext.tsx +4 -4
  228. package/src/NavigationIndependentTree.tsx +23 -12
  229. package/src/NavigationProvider.tsx +26 -15
  230. package/src/NavigationStateContext.tsx +9 -6
  231. package/src/PreventRemoveContext.tsx +1 -0
  232. package/src/PreventRemoveProvider.tsx +51 -46
  233. package/src/SceneView.tsx +11 -39
  234. package/src/StaticNavigation.tsx +659 -307
  235. package/src/StaticTreeContext.tsx +7 -0
  236. package/src/checkSerializable.tsx +35 -19
  237. package/src/createNavigationContainerRef.tsx +12 -9
  238. package/src/createNavigatorFactory.tsx +92 -8
  239. package/src/findFocusedRoute.tsx +16 -16
  240. package/src/getActionFromState.tsx +87 -51
  241. package/src/getPathFromState.tsx +254 -128
  242. package/src/getPatternParts.tsx +25 -4
  243. package/src/getStateFromPath.tsx +821 -331
  244. package/src/getStateFromRouteParams.tsx +59 -0
  245. package/src/index.tsx +55 -5
  246. package/src/theming/useTheme.tsx +1 -1
  247. package/src/types.tsx +798 -373
  248. package/src/useDescriptors.tsx +31 -14
  249. package/src/useEventEmitter.tsx +52 -38
  250. package/src/useFocusEvents.tsx +15 -7
  251. package/src/useFocusedListenersChildrenAdapter.tsx +2 -2
  252. package/src/useIsFocused.tsx +14 -21
  253. package/src/useKeyedChildListeners.tsx +4 -2
  254. package/src/useLazyValue.tsx +2 -2
  255. package/src/useNavigation.tsx +7 -24
  256. package/src/useNavigationBuilder.tsx +314 -186
  257. package/src/useNavigationCache.tsx +32 -25
  258. package/src/useNavigationHelpers.tsx +27 -10
  259. package/src/useNavigationIndependentTree.tsx +1 -1
  260. package/src/useNavigationState.tsx +168 -69
  261. package/src/useOnAction.tsx +61 -13
  262. package/src/useOnGetState.tsx +5 -3
  263. package/src/useOnPreventRemove.tsx +75 -42
  264. package/src/useOnRouteFocus.tsx +10 -12
  265. package/src/useOptionsGetters.tsx +11 -5
  266. package/src/usePreventRemove.tsx +12 -3
  267. package/src/usePreventRemoveContext.tsx +1 -1
  268. package/src/useRegisterNavigator.tsx +2 -2
  269. package/src/useRoute.tsx +20 -6
  270. package/src/useRouteCache.tsx +22 -16
  271. package/src/useScheduleUpdate.tsx +9 -10
  272. package/src/useStateForPath.tsx +1 -1
  273. package/src/useSyncState.tsx +35 -9
  274. package/src/utilities.tsx +144 -31
  275. package/src/validatePathConfig.tsx +86 -57
  276. package/lib/module/CurrentRenderContext.js +0 -10
  277. package/lib/module/CurrentRenderContext.js.map +0 -1
  278. package/lib/module/useCurrentRender.js +0 -19
  279. package/lib/module/useCurrentRender.js.map +0 -1
  280. package/lib/typescript/src/CurrentRenderContext.d.ts +0 -9
  281. package/lib/typescript/src/CurrentRenderContext.d.ts.map +0 -1
  282. package/lib/typescript/src/useCurrentRender.d.ts +0 -14
  283. package/lib/typescript/src/useCurrentRender.d.ts.map +0 -1
  284. package/src/CurrentRenderContext.tsx +0 -9
  285. package/src/useCurrentRender.tsx +0 -31
@@ -15,10 +15,10 @@ 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
- import { isRecordEqual } from './isRecordEqual';
22
22
  import { NavigationBuilderContext } from './NavigationBuilderContext';
23
23
  import { NavigationHelpersContext } from './NavigationHelpersContext';
24
24
  import { NavigationMetaContext } from './NavigationMetaContext';
@@ -37,11 +37,11 @@ import {
37
37
  import { UnhandledActionContext } from './UnhandledActionContext';
38
38
  import { useChildListeners } from './useChildListeners';
39
39
  import { useComponent } from './useComponent';
40
- import { useCurrentRender } from './useCurrentRender';
41
40
  import { type ScreenConfigWithParent, useDescriptors } from './useDescriptors';
42
41
  import { useEventEmitter } from './useEventEmitter';
43
42
  import { useFocusedListenersChildrenAdapter } from './useFocusedListenersChildrenAdapter';
44
43
  import { useFocusEvents } from './useFocusEvents';
44
+ import { FocusedRouteKeyContext } from './useIsFocused';
45
45
  import { useKeyedChildListeners } from './useKeyedChildListeners';
46
46
  import { useLazyValue } from './useLazyValue';
47
47
  import { useNavigationHelpers } from './useNavigationHelpers';
@@ -58,9 +58,17 @@ PrivateValueStore;
58
58
 
59
59
  type NavigatorRoute = {
60
60
  key: string;
61
- params?: NavigatorScreenParams<ParamListBase>;
61
+ params?: NavigatorScreenParams<ParamListBase> | undefined;
62
62
  };
63
63
 
64
+ const isNavigationState = (
65
+ state: unknown
66
+ ): state is NavigationState | PartialState<NavigationState> =>
67
+ state != null &&
68
+ typeof state === 'object' &&
69
+ 'routes' in state &&
70
+ Array.isArray(state.routes);
71
+
64
72
  const isScreen = (
65
73
  child: React.ReactElement<unknown>
66
74
  ): child is React.ReactElement<{
@@ -95,7 +103,7 @@ const getRouteConfigsFromChildren = <
95
103
  EventMap extends EventMapBase,
96
104
  >(
97
105
  children: React.ReactNode,
98
- groupKey?: string,
106
+ groupKeys: string[] = [],
99
107
  groupOptions?: ScreenConfigWithParent<
100
108
  State,
101
109
  ScreenOptions,
@@ -138,7 +146,7 @@ const getRouteConfigsFromChildren = <
138
146
  }
139
147
 
140
148
  acc.push({
141
- keys: [groupKey, child.props.navigationKey],
149
+ keys: [...groupKeys, child.props.navigationKey],
142
150
  options: groupOptions,
143
151
  layout: groupLayout,
144
152
  props: child.props as RouteConfig<
@@ -168,7 +176,9 @@ const getRouteConfigsFromChildren = <
168
176
  acc.push(
169
177
  ...getRouteConfigsFromChildren<State, ScreenOptions, EventMap>(
170
178
  child.props.children as React.ReactNode,
171
- child.props.navigationKey,
179
+ child.props.navigationKey != null
180
+ ? [...groupKeys, child.props.navigationKey]
181
+ : groupKeys,
172
182
  // FIXME
173
183
  // @ts-expect-error: add validation
174
184
  child.type !== Group
@@ -276,9 +286,24 @@ const getRouteConfigsFromChildren = <
276
286
  return configs;
277
287
  };
278
288
 
279
- const getStateFromParams = (params: NavigatorRoute['params']) => {
280
- if (params?.state != null) {
281
- return params.state;
289
+ const getStateFromParams = (
290
+ params: NavigatorRoute['params'],
291
+ type: string
292
+ ): NavigationState | PartialState<NavigationState> | undefined => {
293
+ const state = params?.state;
294
+
295
+ if (isNavigationState(state)) {
296
+ if (state.type != null && state.type !== type) {
297
+ if (process.env.NODE_ENV !== 'production') {
298
+ console.error(
299
+ `The state passed in the params is for a navigator of type '${state.type}', but it was passed to a navigator of type '${type}'. This can happen if the state was meant for a different navigator. The state will be discarded.`
300
+ );
301
+ }
302
+
303
+ return undefined;
304
+ }
305
+
306
+ return state;
282
307
  } else if (typeof params?.screen === 'string' && params?.initial !== false) {
283
308
  return {
284
309
  routes: [
@@ -320,9 +345,14 @@ export function useNavigationBuilder<
320
345
  ) {
321
346
  const navigatorKey = useRegisterNavigator();
322
347
 
323
- const route = React.useContext(NavigationRouteContext) as
324
- | NavigatorRoute
325
- | undefined;
348
+ const route = React.use(NavigationRouteContext) as NavigatorRoute | undefined;
349
+
350
+ const consumedParams = React.use(ConsumedParamsContext);
351
+
352
+ const isNestedParamsConsumed =
353
+ typeof route?.params === 'object' && route.params != null
354
+ ? consumedParams?.has(route.params)
355
+ : false;
326
356
 
327
357
  const {
328
358
  children,
@@ -366,44 +396,7 @@ export function useNavigationBuilder<
366
396
  return original;
367
397
  });
368
398
 
369
- const screens = routeConfigs.reduce<
370
- Record<string, ScreenConfigWithParent<State, ScreenOptions, EventMap>>
371
- >((acc, config) => {
372
- if (config.props.name in acc) {
373
- throw new Error(
374
- `A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${config.props.name}')`
375
- );
376
- }
377
-
378
- acc[config.props.name] = config;
379
- return acc;
380
- }, {});
381
-
382
399
  const routeNames = routeConfigs.map((config) => config.props.name);
383
- const routeKeyList = routeNames.reduce<Record<string, React.Key | undefined>>(
384
- (acc, curr) => {
385
- acc[curr] = screens[curr].keys.map((key) => key ?? '').join(':');
386
- return acc;
387
- },
388
- {}
389
- );
390
- const routeParamList = routeNames.reduce<Record<string, object | undefined>>(
391
- (acc, curr) => {
392
- const { initialParams } = screens[curr].props;
393
- acc[curr] = initialParams;
394
- return acc;
395
- },
396
- {}
397
- );
398
- const routeGetIdList = routeNames.reduce<
399
- RouterConfigOptions['routeGetIdList']
400
- >(
401
- (acc, curr) =>
402
- Object.assign(acc, {
403
- [curr]: screens[curr].props.getId,
404
- }),
405
- {}
406
- );
407
400
 
408
401
  if (!routeNames.length) {
409
402
  throw new Error(
@@ -411,6 +404,31 @@ export function useNavigationBuilder<
411
404
  );
412
405
  }
413
406
 
407
+ const screens: Record<
408
+ string,
409
+ ScreenConfigWithParent<State, ScreenOptions, EventMap>
410
+ > = {};
411
+
412
+ const routeKeyList: Record<string, (string | undefined)[]> = {};
413
+ const routeParamList: Record<string, object | undefined> = {};
414
+ const routeGetIdList: RouterConfigOptions['routeGetIdList'] = {};
415
+
416
+ for (const config of routeConfigs) {
417
+ const name = config.props.name;
418
+
419
+ if (name in screens) {
420
+ throw new Error(
421
+ `A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${name}')`
422
+ );
423
+ }
424
+
425
+ screens[name] = config;
426
+ routeKeyList[name] = config.keys;
427
+ routeParamList[name] = config.props.initialParams;
428
+
429
+ Object.assign(routeGetIdList, { [name]: config.props.getId });
430
+ }
431
+
414
432
  const isStateValid = React.useCallback(
415
433
  (state: NavigationState | PartialState<NavigationState>) =>
416
434
  state.type === undefined || state.type === router.type,
@@ -418,7 +436,9 @@ export function useNavigationBuilder<
418
436
  );
419
437
 
420
438
  const isStateInitialized = React.useCallback(
421
- (state: NavigationState | PartialState<NavigationState> | undefined) =>
439
+ <T extends NavigationState>(
440
+ state: T | PartialState<T> | undefined
441
+ ): state is T =>
422
442
  state !== undefined && state.stale === false && isStateValid(state),
423
443
  [isStateValid]
424
444
  );
@@ -433,19 +453,30 @@ export function useNavigationBuilder<
433
453
  state: currentState,
434
454
  getState: getCurrentState,
435
455
  setState: setCurrentState,
456
+ subscribe,
436
457
  setKey,
437
458
  getKey,
438
459
  getIsInitial,
439
- } = React.useContext(NavigationStateContext);
460
+ } = React.use(NavigationStateContext);
440
461
 
441
- const stateCleanedUp = React.useRef(false);
462
+ const { onEmitEvent, getIsStateEmitted } = React.use(
463
+ NavigationBuilderContext
464
+ );
465
+
466
+ const stateCleanupRef = React.useRef<boolean>(false);
467
+ const lastStateRef = React.useRef<State | PartialState<State> | undefined>(
468
+ undefined
469
+ );
442
470
 
443
471
  const setState = useLatestCallback(
444
- (state: NavigationState | PartialState<NavigationState> | undefined) => {
445
- if (stateCleanedUp.current) {
472
+ (state: State | PartialState<State> | undefined) => {
473
+ if (stateCleanupRef.current) {
474
+ // Store the state locally in case the current navigator is in `Activity`
475
+ lastStateRef.current = state;
476
+
446
477
  // State might have been already cleaned up due to unmount
447
- // We do not want to expose API allowing to override this
448
- // This would lead to old data preservation on main navigator unmount
478
+ // We don't want to update `route.state` in parent
479
+ // Otherwise it will be reused if a new navigator gets mounted
449
480
  return;
450
481
  }
451
482
 
@@ -457,15 +488,36 @@ export function useNavigationBuilder<
457
488
  stateBeforeInitialization,
458
489
  initializedState,
459
490
  isFirstStateInitialization,
491
+ paramsUsedForInitialization,
460
492
  ] = React.useMemo((): [
461
493
  PartialState<State> | undefined,
462
494
  State | undefined,
463
495
  boolean,
496
+ object | undefined,
464
497
  ] => {
498
+ // If the state was already cleaned up, but we have it stored in ref,
499
+ // It likely got cleaned up due to `<Activity mode="hidden">`
500
+ // We should reuse this state to avoid remounting screens
501
+ if (
502
+ stateCleanupRef.current &&
503
+ lastStateRef.current &&
504
+ isStateValid(lastStateRef.current)
505
+ ) {
506
+ const state: State = isStateInitialized(lastStateRef.current)
507
+ ? lastStateRef.current
508
+ : router.getRehydratedState(lastStateRef.current, {
509
+ routeNames,
510
+ routeParamList,
511
+ routeGetIdList,
512
+ });
513
+
514
+ return [undefined, state, false, undefined];
515
+ }
516
+
465
517
  const initialRouteParamList = routeNames.reduce<
466
518
  Record<string, object | undefined>
467
519
  >((acc, curr) => {
468
- const { initialParams } = screens[curr].props;
520
+ const { initialParams } = screens[curr]?.props ?? {};
469
521
  const initialParamsFromParams =
470
522
  route?.params?.state == null &&
471
523
  route?.params?.initial !== false &&
@@ -494,7 +546,8 @@ export function useNavigationBuilder<
494
546
  !(
495
547
  typeof route?.params?.screen === 'string' &&
496
548
  route?.params?.initial !== false
497
- )
549
+ ) &&
550
+ !isNestedParamsConsumed
498
551
  ) {
499
552
  return [
500
553
  undefined,
@@ -504,28 +557,48 @@ export function useNavigationBuilder<
504
557
  routeGetIdList,
505
558
  }),
506
559
  true,
560
+ undefined,
507
561
  ];
508
562
  } else {
509
- const stateFromParams = getStateFromParams(route?.params);
510
- const stateBeforeInitialization = (stateFromParams ??
511
- currentState) as PartialState<State>;
512
- const hydratedState = router.getRehydratedState(
513
- stateBeforeInitialization,
514
- {
515
- routeNames,
516
- routeParamList: initialRouteParamList,
517
- routeGetIdList,
518
- }
519
- );
563
+ const paramsForState = isNestedParamsConsumed ? undefined : route?.params;
564
+ const stateFromParams = paramsForState
565
+ ? getStateFromParams(paramsForState, router.type)
566
+ : undefined;
567
+
568
+ const stateBeforeInitialization = (stateFromParams ?? currentState) as
569
+ | PartialState<State>
570
+ | undefined;
571
+
572
+ const hydratedState =
573
+ stateBeforeInitialization == null
574
+ ? router.getInitialState({
575
+ routeNames,
576
+ routeParamList: initialRouteParamList,
577
+ routeGetIdList,
578
+ })
579
+ : router.getRehydratedState(stateBeforeInitialization, {
580
+ routeNames,
581
+ routeParamList: initialRouteParamList,
582
+ routeGetIdList,
583
+ });
520
584
 
521
585
  if (
586
+ stateBeforeInitialization != null &&
587
+ isStateValid(stateBeforeInitialization) &&
522
588
  options.routeNamesChangeBehavior === 'lastUnhandled' &&
523
589
  doesStateHaveOnlyInvalidRoutes(stateBeforeInitialization)
524
590
  ) {
525
- return [stateBeforeInitialization, hydratedState, true];
591
+ return [stateBeforeInitialization, hydratedState, true, paramsForState];
526
592
  }
527
593
 
528
- return [undefined, hydratedState, false];
594
+ return [
595
+ undefined,
596
+ hydratedState,
597
+ false,
598
+ // Current state is already initialized
599
+ // So params weren't used, and need to be handled later
600
+ isStateInitialized(currentState) ? undefined : paramsForState,
601
+ ];
529
602
  }
530
603
  // We explicitly don't include routeNames, route.params etc. in the dep list
531
604
  // below. We want to avoid forcing a new state to be calculated in those cases
@@ -537,12 +610,22 @@ export function useNavigationBuilder<
537
610
 
538
611
  const previousRouteKeyListRef = React.useRef(routeKeyList);
539
612
 
540
- React.useEffect(() => {
613
+ React.useInsertionEffect(() => {
541
614
  previousRouteKeyListRef.current = routeKeyList;
542
615
  });
543
616
 
544
617
  const previousRouteKeyList = previousRouteKeyListRef.current;
545
618
 
619
+ // Names of the screens whose navigation key changed since the last render
620
+ const routeKeyChanges = Object.keys(routeKeyList).filter((name) => {
621
+ const current = routeKeyList[name];
622
+ const previous = previousRouteKeyList[name];
623
+
624
+ return (
625
+ current != null && previous != null && !isArrayEqual(current, previous)
626
+ );
627
+ });
628
+
546
629
  const [unhandledState, setUnhandledState] = React.useState<
547
630
  NavigationState | PartialState<NavigationState> | undefined
548
631
  >(stateBeforeInitialization);
@@ -570,10 +653,14 @@ export function useNavigationBuilder<
570
653
  let nextState: State = state;
571
654
  let shouldClearUnhandledState = false;
572
655
 
573
- // Previously unhandled state is now valid again
574
- // And current state no longer has any valid routes
575
- // We should reuse the unhandled state instead of re-calculating the state
576
- if (
656
+ if (unhandledState != null && !isStateValid(unhandledState)) {
657
+ // The stored unhandled state is for a different type of navigator
658
+ // It can't be used with this navigator, so we discard it
659
+ setUnhandledState(undefined);
660
+ } else if (
661
+ // Previously unhandled state is now valid again
662
+ // And current state no longer has any valid routes
663
+ // We should reuse the unhandled state instead of re-calculating the state
577
664
  unhandledState?.routes.every((r) => routeNames.includes(r.name)) &&
578
665
  state?.routes.every((r) => !routeNames.includes(r.name))
579
666
  ) {
@@ -588,71 +675,68 @@ export function useNavigationBuilder<
588
675
  );
589
676
  } else if (
590
677
  !isArrayEqual(state.routeNames, routeNames) ||
591
- !isRecordEqual(routeKeyList, previousRouteKeyList)
678
+ routeKeyChanges.length !== 0
592
679
  ) {
593
680
  // When the list of route names change, the router should handle it to remove invalid routes
594
681
  nextState = router.getStateForRouteNamesChange(state, {
595
682
  routeNames,
596
683
  routeParamList,
597
684
  routeGetIdList,
598
- routeKeyChanges: Object.keys(routeKeyList).filter(
599
- (name) =>
600
- name in previousRouteKeyList &&
601
- routeKeyList[name] !== previousRouteKeyList[name]
602
- ),
685
+ routeKeyChanges,
603
686
  });
604
687
  }
605
688
 
606
- const previousNestedParamsRef = React.useRef(route?.params);
689
+ let didConsumeNestedParams = route?.params === paramsUsedForInitialization;
607
690
 
608
- React.useEffect(() => {
609
- previousNestedParamsRef.current = route?.params;
610
- }, [route?.params]);
691
+ if (route?.params && !didConsumeNestedParams) {
692
+ let action: CommonActions.Action | undefined;
611
693
 
612
- if (route?.params) {
613
- const previousParams = previousNestedParamsRef.current;
694
+ if (isNavigationState(route.params.state) && !isNestedParamsConsumed) {
695
+ didConsumeNestedParams = true;
614
696
 
615
- let action: CommonActions.Action | undefined;
697
+ const stateFromParams = getStateFromParams(route.params, router.type);
616
698
 
617
- if (
618
- typeof route.params.state === 'object' &&
619
- route.params.state != null &&
620
- route.params !== previousParams
621
- ) {
622
- if (
623
- options.routeNamesChangeBehavior === 'lastUnhandled' &&
624
- doesStateHaveOnlyInvalidRoutes(route.params.state)
625
- ) {
626
- if (route.params.state !== unhandledState) {
627
- setUnhandledState(route.params.state);
699
+ if (stateFromParams != null) {
700
+ if (
701
+ options.routeNamesChangeBehavior === 'lastUnhandled' &&
702
+ doesStateHaveOnlyInvalidRoutes(stateFromParams)
703
+ ) {
704
+ if (stateFromParams !== unhandledState) {
705
+ setUnhandledState(stateFromParams);
706
+ }
707
+ } else {
708
+ // If the route was updated with new state, we should reset to it
709
+ action = CommonActions.reset(stateFromParams);
628
710
  }
629
- } else {
630
- // If the route was updated with new state, we should reset to it
631
- action = CommonActions.reset(route.params.state);
632
711
  }
633
712
  } else if (
634
713
  typeof route.params.screen === 'string' &&
635
714
  ((route.params.initial === false && isFirstStateInitialization) ||
636
- route.params !== previousParams)
715
+ !isNestedParamsConsumed)
637
716
  ) {
717
+ didConsumeNestedParams = true;
718
+
638
719
  if (
639
720
  options.routeNamesChangeBehavior === 'lastUnhandled' &&
640
721
  !routeNames.includes(route.params.screen)
641
722
  ) {
642
- const state = getStateFromParams(route.params);
723
+ const state = getStateFromParams(route.params, router.type);
643
724
 
644
725
  if (state != null && !deepEqual(state, unhandledState)) {
645
726
  setUnhandledState(state);
646
727
  }
647
728
  } else {
648
729
  // If the route was updated with new screen name and/or params, we should navigate there
649
- action = CommonActions.navigate({
650
- name: route.params.screen,
651
- params: route.params.params,
652
- path: route.params.path,
653
- merge: route.params.merge,
654
- pop: route.params.pop,
655
- });
730
+ action = {
731
+ type: 'NAVIGATE',
732
+ payload: {
733
+ name: route.params.screen,
734
+ params: route.params.params,
735
+ path: route.params.path,
736
+ merge: route.params.merge,
737
+ pop: route.params.pop,
738
+ },
739
+ } as const;
656
740
  }
657
741
  }
658
742
 
@@ -665,6 +749,18 @@ export function useNavigationBuilder<
665
749
  })
666
750
  : null;
667
751
 
752
+ if (
753
+ process.env.NODE_ENV !== 'production' &&
754
+ action != null &&
755
+ updatedState === null
756
+ ) {
757
+ const names = routeNames.map((name) => `'${name}'`).join(', ');
758
+
759
+ console.error(
760
+ `The 'state' or 'screen' passed in the params couldn't be applied to the navigator with the screens: ${names}. This can happen if the params contain names of screens that don't exist in the navigator. The navigation specified in the params will be ignored.`
761
+ );
762
+ }
763
+
668
764
  nextState =
669
765
  updatedState !== null
670
766
  ? router.getRehydratedState(updatedState, {
@@ -675,14 +771,22 @@ export function useNavigationBuilder<
675
771
  : nextState;
676
772
  }
677
773
 
678
- const shouldUpdate =
679
- state !== nextState ||
680
- typeof route?.params?.state === 'object' ||
681
- typeof route?.params?.screen === 'string';
774
+ React.useEffect(() => {
775
+ if (
776
+ consumedParams &&
777
+ didConsumeNestedParams &&
778
+ typeof route?.params === 'object' &&
779
+ route.params != null
780
+ ) {
781
+ consumedParams.set(route.params, true);
782
+ }
783
+ }, [consumedParams, didConsumeNestedParams, route?.params]);
784
+
785
+ const shouldUpdate = state !== nextState;
682
786
 
683
787
  useScheduleUpdate(() => {
684
788
  if (shouldUpdate) {
685
- // If the state needs to be updated, we'll schedule an update
789
+ // Schedule an update if the state needs to be updated
686
790
  setState(nextState);
687
791
 
688
792
  if (shouldClearUnhandledState) {
@@ -696,43 +800,59 @@ export function useNavigationBuilder<
696
800
  // So we override the state object we return to use the latest state as soon as possible
697
801
  state = nextState;
698
802
 
803
+ // Last state to reuse if component gets cleaned up due to `<Activity mode="hidden">`
804
+ React.useEffect(() => {
805
+ lastStateRef.current = state;
806
+ });
807
+
808
+ const lastNotifiedStateRef = React.useRef<State | null>(null);
809
+
699
810
  React.useEffect(() => {
700
811
  // In strict mode, React will double-invoke effects.
701
812
  // So we need to reset the flag if component was not unmounted
702
- stateCleanedUp.current = false;
813
+ stateCleanupRef.current = false;
703
814
 
704
815
  setKey(navigatorKey);
705
816
 
706
- if (!getIsInitial()) {
707
- // If it's not initial render, we need to update the state
708
- // This will make sure that our container gets notifier of state changes due to new mounts
817
+ if (
818
+ (!getIsInitial() || getIsStateEmitted()) &&
819
+ lastNotifiedStateRef.current !== state
820
+ ) {
821
+ // We need to notify the state update in these scenarios:
822
+ // 1. If it's not the initial render of the component containing the navigator
823
+ // 2. If the container has already emitted state before the navigator mounted
824
+ //
825
+ // This will make sure that the container gets notified of state changes due to new mounts
709
826
  // This is necessary for proper screen tracking, URL updates etc.
710
- setState(nextState);
827
+ //
828
+ // During hydration, a nested navigator may mount after the container's initial state event.
829
+ // Or mount could be delayed due to conditional rendering, suspense etc.
830
+ // So only checking for the initial render is not enough.
831
+ // We also need to propagate the navigator's initial state when it mounts later,
832
+ // so the container is notified that the nested state is now available.
833
+ //
834
+ // We only notify if the state is different from what we already notified
835
+ // Otherwise, this goes into a loop when inside `<Activity mode="hidden">`
836
+ setState(state);
837
+
838
+ lastNotifiedStateRef.current = state;
711
839
  }
712
840
 
713
841
  return () => {
714
842
  // We need to clean up state for this navigator on unmount
715
843
  if (getCurrentState() !== undefined && getKey() === navigatorKey) {
716
844
  setCurrentState(undefined);
717
- stateCleanedUp.current = true;
845
+ stateCleanupRef.current = true;
718
846
  }
847
+
848
+ // Reset so that StrictMode's second mount re-propagates state correctly.
849
+ // Without this, the guard above sees the same reference and skips setState,
850
+ // causing the initial state to be lost after cleanup wipes the container state.
851
+ lastNotifiedStateRef.current = null;
719
852
  };
720
853
  // eslint-disable-next-line react-hooks/exhaustive-deps
721
854
  }, []);
722
855
 
723
- // In some cases (e.g. route names change), internal state might have changed
724
- // But it hasn't been committed yet, so hasn't propagated to the sync external store
725
- // During this time, we need to return the internal state in `getState`
726
- // Otherwise it can result in inconsistent state during render in children
727
- // To avoid this, we use a ref for render phase, and immediately clear it on commit
728
- const stateRef = React.useRef<State | null>(state);
729
-
730
- stateRef.current = state;
731
-
732
- React.useLayoutEffect(() => {
733
- stateRef.current = null;
734
- });
735
-
736
856
  const getState = useLatestCallback((): State => {
737
857
  const currentState = getCurrentState();
738
858
 
@@ -743,8 +863,6 @@ export function useNavigationBuilder<
743
863
  );
744
864
  });
745
865
 
746
- const { onEmitEvent } = React.useContext(NavigationBuilderContext);
747
-
748
866
  const emitter = useEventEmitter<EventMapCore<State>>((e) => {
749
867
  const routeNames = [];
750
868
 
@@ -767,43 +885,48 @@ export function useNavigationBuilder<
767
885
  return;
768
886
  }
769
887
 
770
- const navigation = descriptors[route.key].navigation;
771
-
772
- const listeners = ([] as (((e: any) => void) | undefined)[])
773
- .concat(
774
- // Get an array of listeners for all screens + common listeners on navigator
775
- ...[
776
- screenListeners,
777
- ...routeNames.map((name) => {
778
- const { listeners } = screens[name].props;
779
- return listeners;
780
- }),
781
- ].map((listeners) => {
782
- const map =
783
- typeof listeners === 'function'
784
- ? listeners({ route: route as any, navigation })
785
- : listeners;
786
-
787
- return map
788
- ? Object.keys(map)
789
- .filter((type) => type === e.type)
790
- .map((type) => map?.[type])
791
- : undefined;
792
- })
793
- )
794
- // We don't want same listener to be called multiple times for same event
795
- // So we remove any duplicate functions from the array
796
- .filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
888
+ const hasPerScreenListeners = routeNames.some(
889
+ (name) => screens[name]?.props.listeners != null
890
+ );
797
891
 
798
- listeners.forEach((listener) => listener?.(e));
892
+ if (screenListeners != null || hasPerScreenListeners) {
893
+ const descriptor = descriptors[route.key];
799
894
 
800
- onEmitEvent({
801
- type: e.type,
802
- data: e.data,
803
- target: e.target,
804
- defaultPrevented: e.defaultPrevented,
805
- });
806
- });
895
+ if (descriptor == null) {
896
+ throw new Error(`Couldn't find a descriptor for route '${route.key}'.`);
897
+ }
898
+
899
+ const navigation = descriptor.navigation;
900
+
901
+ const listeners = ([] as (((e: any) => void) | undefined)[])
902
+ .concat(
903
+ // Get an array of listeners for all screens + common listeners on navigator
904
+ ...[
905
+ screenListeners,
906
+ ...routeNames.map((name) => {
907
+ const { listeners } = screens[name]?.props ?? {};
908
+ return listeners;
909
+ }),
910
+ ].map((listeners) => {
911
+ const map =
912
+ typeof listeners === 'function'
913
+ ? listeners({ route: route as any, navigation })
914
+ : listeners;
915
+
916
+ return map
917
+ ? Object.keys(map)
918
+ .filter((type) => type === e.type)
919
+ .map((type) => map?.[type])
920
+ : undefined;
921
+ })
922
+ )
923
+ // We don't want same listener to be called multiple times for same event
924
+ // So we remove any duplicate functions from the array
925
+ .filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
926
+
927
+ listeners.forEach((listener) => listener?.(e));
928
+ }
929
+ }, onEmitEvent);
807
930
 
808
931
  useFocusEvents({ state, emitter });
809
932
 
@@ -837,7 +960,7 @@ export function useNavigationBuilder<
837
960
  setState,
838
961
  });
839
962
 
840
- const onUnhandledActionParent = React.useContext(UnhandledActionContext);
963
+ const onUnhandledActionParent = React.use(UnhandledActionContext);
841
964
 
842
965
  const onUnhandledAction = useLatestCallback((action: NavigationAction) => {
843
966
  if (
@@ -882,9 +1005,9 @@ export function useNavigationBuilder<
882
1005
  onAction,
883
1006
  onUnhandledAction,
884
1007
  getState,
1008
+ state,
885
1009
  emitter,
886
1010
  router,
887
- stateRef,
888
1011
  });
889
1012
 
890
1013
  useFocusedListenersChildrenAdapter({
@@ -903,9 +1026,7 @@ export function useNavigationBuilder<
903
1026
  ScreenOptions,
904
1027
  EventMap
905
1028
  >({
906
- routes: router.getRoutesFromState
907
- ? router.getRoutesFromState(state)
908
- : state.routes,
1029
+ routes: state.routes,
909
1030
  screens,
910
1031
  navigation,
911
1032
  screenOptions,
@@ -913,6 +1034,7 @@ export function useNavigationBuilder<
913
1034
  onAction,
914
1035
  getState,
915
1036
  setState,
1037
+ subscribe,
916
1038
  onRouteFocus,
917
1039
  addListener,
918
1040
  addKeyedListener,
@@ -921,13 +1043,13 @@ export function useNavigationBuilder<
921
1043
  emitter,
922
1044
  });
923
1045
 
924
- useCurrentRender({
925
- state,
926
- navigation,
927
- descriptors,
928
- });
929
-
930
1046
  const NavigationContent = useComponent((children: React.ReactNode) => {
1047
+ const focusedRoute = state.routes[state.index];
1048
+
1049
+ if (focusedRoute == null) {
1050
+ throw new Error(`Couldn't find a route at index ${state.index}.`);
1051
+ }
1052
+
931
1053
  const element =
932
1054
  layout != null
933
1055
  ? layout({
@@ -941,8 +1063,14 @@ export function useNavigationBuilder<
941
1063
  return (
942
1064
  <NavigationMetaContext.Provider value={undefined}>
943
1065
  <NavigationHelpersContext.Provider value={navigation}>
944
- <NavigationStateListenerProvider state={state}>
945
- <PreventRemoveProvider>{element}</PreventRemoveProvider>
1066
+ <NavigationStateListenerProvider
1067
+ state={state}
1068
+ getState={navigation.getState}
1069
+ subscribe={subscribe}
1070
+ >
1071
+ <FocusedRouteKeyContext.Provider value={focusedRoute.key}>
1072
+ <PreventRemoveProvider>{element}</PreventRemoveProvider>
1073
+ </FocusedRouteKeyContext.Provider>
946
1074
  </NavigationStateListenerProvider>
947
1075
  </NavigationHelpersContext.Provider>
948
1076
  </NavigationMetaContext.Provider>