@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
@@ -5,10 +5,10 @@ import deepEqual from 'fast-deep-equal';
5
5
  import * as React from 'react';
6
6
  import { isValidElementType } from 'react-is';
7
7
  import useLatestCallback from 'use-latest-callback';
8
+ import { ConsumedParamsContext } from "./ConsumedParamsContext.js";
8
9
  import { deepFreeze } from "./deepFreeze.js";
9
10
  import { Group } from "./Group.js";
10
11
  import { isArrayEqual } from "./isArrayEqual.js";
11
- import { isRecordEqual } from "./isRecordEqual.js";
12
12
  import { NavigationBuilderContext } from "./NavigationBuilderContext.js";
13
13
  import { NavigationHelpersContext } from "./NavigationHelpersContext.js";
14
14
  import { NavigationMetaContext } from "./NavigationMetaContext.js";
@@ -20,11 +20,11 @@ import { PrivateValueStore } from "./types.js";
20
20
  import { UnhandledActionContext } from "./UnhandledActionContext.js";
21
21
  import { useChildListeners } from "./useChildListeners.js";
22
22
  import { useComponent } from "./useComponent.js";
23
- import { useCurrentRender } from "./useCurrentRender.js";
24
23
  import { useDescriptors } from "./useDescriptors.js";
25
24
  import { useEventEmitter } from "./useEventEmitter.js";
26
25
  import { useFocusedListenersChildrenAdapter } from "./useFocusedListenersChildrenAdapter.js";
27
26
  import { useFocusEvents } from "./useFocusEvents.js";
27
+ import { FocusedRouteKeyContext } from "./useIsFocused.js";
28
28
  import { useKeyedChildListeners } from "./useKeyedChildListeners.js";
29
29
  import { useLazyValue } from "./useLazyValue.js";
30
30
  import { useNavigationHelpers } from "./useNavigationHelpers.js";
@@ -39,6 +39,7 @@ import { useScheduleUpdate } from "./useScheduleUpdate.js";
39
39
  // eslint-disable-next-line @typescript-eslint/no-unused-expressions
40
40
  import { jsx as _jsx } from "react/jsx-runtime";
41
41
  PrivateValueStore;
42
+ const isNavigationState = state => state != null && typeof state === 'object' && 'routes' in state && Array.isArray(state.routes);
42
43
  const isScreen = child => {
43
44
  return child.type === Screen;
44
45
  };
@@ -52,7 +53,7 @@ const isValidKey = key => key === undefined || typeof key === 'string' && key !=
52
53
  *
53
54
  * @param children React Elements to extract the config from.
54
55
  */
55
- const getRouteConfigsFromChildren = (children, groupKey, groupOptions, groupLayout) => {
56
+ const getRouteConfigsFromChildren = (children, groupKeys = [], groupOptions, groupLayout) => {
56
57
  const configs = React.Children.toArray(children).reduce((acc, child) => {
57
58
  if (/*#__PURE__*/React.isValidElement(child)) {
58
59
  if (isScreen(child)) {
@@ -69,7 +70,7 @@ const getRouteConfigsFromChildren = (children, groupKey, groupOptions, groupLayo
69
70
  throw new Error(`Got an invalid 'navigationKey' prop (${JSON.stringify(child.props.navigationKey)}) for the screen '${child.props.name}'. It must be a non-empty string or 'undefined'.`);
70
71
  }
71
72
  acc.push({
72
- keys: [groupKey, child.props.navigationKey],
73
+ keys: [...groupKeys, child.props.navigationKey],
73
74
  options: groupOptions,
74
75
  layout: groupLayout,
75
76
  props: child.props
@@ -83,14 +84,14 @@ const getRouteConfigsFromChildren = (children, groupKey, groupOptions, groupLayo
83
84
 
84
85
  // When we encounter a fragment or group, we need to dive into its children to extract the configs
85
86
  // This is handy to conditionally define a group of screens
86
- acc.push(...getRouteConfigsFromChildren(child.props.children, child.props.navigationKey,
87
+ acc.push(...getRouteConfigsFromChildren(child.props.children, child.props.navigationKey != null ? [...groupKeys, child.props.navigationKey] : groupKeys,
87
88
  // FIXME
88
89
  // @ts-expect-error: add validation
89
90
  child.type !== Group ? groupOptions : groupOptions != null ? [...groupOptions, child.props.screenOptions] : [child.props.screenOptions], typeof child.props.screenLayout === 'function' ? child.props.screenLayout : groupLayout));
90
91
  return acc;
91
92
  }
92
93
  }
93
- throw new Error(`A navigator can only contain 'Screen', 'Group' or 'React.Fragment' as its direct children (found ${/*#__PURE__*/React.isValidElement(child) ? `'${typeof child.type === 'string' ? child.type : child.type?.name}'${child.props != null && typeof child.props === 'object' && 'name' in child.props && child.props?.name ? ` for the screen '${child.props.name}'` : ''}` : typeof child === 'object' ? JSON.stringify(child) : `'${String(child)}'`}). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.`);
94
+ throw new Error(`A navigator can only contain 'Screen', 'Group' or 'React.Fragment' as its direct children (found ${ /*#__PURE__*/React.isValidElement(child) ? `'${typeof child.type === 'string' ? child.type : child.type?.name}'${child.props != null && typeof child.props === 'object' && 'name' in child.props && child.props?.name ? ` for the screen '${child.props.name}'` : ''}` : typeof child === 'object' ? JSON.stringify(child) : `'${String(child)}'`}). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.`);
94
95
  }, []);
95
96
  if (process.env.NODE_ENV !== 'production') {
96
97
  configs.forEach(config => {
@@ -136,9 +137,16 @@ const getRouteConfigsFromChildren = (children, groupKey, groupOptions, groupLayo
136
137
  }
137
138
  return configs;
138
139
  };
139
- const getStateFromParams = params => {
140
- if (params?.state != null) {
141
- return params.state;
140
+ const getStateFromParams = (params, type) => {
141
+ const state = params?.state;
142
+ if (isNavigationState(state)) {
143
+ if (state.type != null && state.type !== type) {
144
+ if (process.env.NODE_ENV !== 'production') {
145
+ console.error(`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.`);
146
+ }
147
+ return undefined;
148
+ }
149
+ return state;
142
150
  } else if (typeof params?.screen === 'string' && params?.initial !== false) {
143
151
  return {
144
152
  routes: [{
@@ -160,7 +168,9 @@ const getStateFromParams = params => {
160
168
  */
161
169
  export function useNavigationBuilder(createRouter, options) {
162
170
  const navigatorKey = useRegisterNavigator();
163
- const route = React.useContext(NavigationRouteContext);
171
+ const route = React.use(NavigationRouteContext);
172
+ const consumedParams = React.use(ConsumedParamsContext);
173
+ const isNestedParamsConsumed = typeof route?.params === 'object' && route.params != null ? consumedParams?.has(route.params) : false;
164
174
  const {
165
175
  children,
166
176
  layout,
@@ -185,31 +195,26 @@ export function useNavigationBuilder(createRouter, options) {
185
195
  }
186
196
  return original;
187
197
  });
188
- const screens = routeConfigs.reduce((acc, config) => {
189
- if (config.props.name in acc) {
190
- throw new Error(`A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${config.props.name}')`);
191
- }
192
- acc[config.props.name] = config;
193
- return acc;
194
- }, {});
195
198
  const routeNames = routeConfigs.map(config => config.props.name);
196
- const routeKeyList = routeNames.reduce((acc, curr) => {
197
- acc[curr] = screens[curr].keys.map(key => key ?? '').join(':');
198
- return acc;
199
- }, {});
200
- const routeParamList = routeNames.reduce((acc, curr) => {
201
- const {
202
- initialParams
203
- } = screens[curr].props;
204
- acc[curr] = initialParams;
205
- return acc;
206
- }, {});
207
- const routeGetIdList = routeNames.reduce((acc, curr) => Object.assign(acc, {
208
- [curr]: screens[curr].props.getId
209
- }), {});
210
199
  if (!routeNames.length) {
211
200
  throw new Error("Couldn't find any screens for the navigator. Have you defined any screens as its children?");
212
201
  }
202
+ const screens = {};
203
+ const routeKeyList = {};
204
+ const routeParamList = {};
205
+ const routeGetIdList = {};
206
+ for (const config of routeConfigs) {
207
+ const name = config.props.name;
208
+ if (name in screens) {
209
+ throw new Error(`A navigator cannot contain multiple 'Screen' components with the same name (found duplicate screen named '${name}')`);
210
+ }
211
+ screens[name] = config;
212
+ routeKeyList[name] = config.keys;
213
+ routeParamList[name] = config.props.initialParams;
214
+ Object.assign(routeGetIdList, {
215
+ [name]: config.props.getId
216
+ });
217
+ }
213
218
  const isStateValid = React.useCallback(state => state.type === undefined || state.type === router.type, [router.type]);
214
219
  const isStateInitialized = React.useCallback(state => state !== undefined && state.stale === false && isStateValid(state), [isStateValid]);
215
220
  const doesStateHaveOnlyInvalidRoutes = React.useCallback(state => state.routes.every(r => !routeNames.includes(r.name)), [routeNames]);
@@ -217,25 +222,45 @@ export function useNavigationBuilder(createRouter, options) {
217
222
  state: currentState,
218
223
  getState: getCurrentState,
219
224
  setState: setCurrentState,
225
+ subscribe,
220
226
  setKey,
221
227
  getKey,
222
228
  getIsInitial
223
- } = React.useContext(NavigationStateContext);
224
- const stateCleanedUp = React.useRef(false);
229
+ } = React.use(NavigationStateContext);
230
+ const {
231
+ onEmitEvent,
232
+ getIsStateEmitted
233
+ } = React.use(NavigationBuilderContext);
234
+ const stateCleanupRef = React.useRef(false);
235
+ const lastStateRef = React.useRef(undefined);
225
236
  const setState = useLatestCallback(state => {
226
- if (stateCleanedUp.current) {
237
+ if (stateCleanupRef.current) {
238
+ // Store the state locally in case the current navigator is in `Activity`
239
+ lastStateRef.current = state;
240
+
227
241
  // State might have been already cleaned up due to unmount
228
- // We do not want to expose API allowing to override this
229
- // This would lead to old data preservation on main navigator unmount
242
+ // We don't want to update `route.state` in parent
243
+ // Otherwise it will be reused if a new navigator gets mounted
230
244
  return;
231
245
  }
232
246
  setCurrentState(state);
233
247
  });
234
- const [stateBeforeInitialization, initializedState, isFirstStateInitialization] = React.useMemo(() => {
248
+ const [stateBeforeInitialization, initializedState, isFirstStateInitialization, paramsUsedForInitialization] = React.useMemo(() => {
249
+ // If the state was already cleaned up, but we have it stored in ref,
250
+ // It likely got cleaned up due to `<Activity mode="hidden">`
251
+ // We should reuse this state to avoid remounting screens
252
+ if (stateCleanupRef.current && lastStateRef.current && isStateValid(lastStateRef.current)) {
253
+ const state = isStateInitialized(lastStateRef.current) ? lastStateRef.current : router.getRehydratedState(lastStateRef.current, {
254
+ routeNames,
255
+ routeParamList,
256
+ routeGetIdList
257
+ });
258
+ return [undefined, state, false, undefined];
259
+ }
235
260
  const initialRouteParamList = routeNames.reduce((acc, curr) => {
236
261
  const {
237
262
  initialParams
238
- } = screens[curr].props;
263
+ } = screens[curr]?.props ?? {};
239
264
  const initialParamsFromParams = route?.params?.state == null && route?.params?.initial !== false && route?.params?.screen === curr ? route.params.params : undefined;
240
265
  acc[curr] = initialParams !== undefined || initialParamsFromParams !== undefined ? {
241
266
  ...initialParams,
@@ -248,24 +273,32 @@ export function useNavigationBuilder(createRouter, options) {
248
273
  // We also need to re-initialize it if the state passed from parent was changed (maybe due to reset)
249
274
  // Otherwise assume that the state was provided as initial state
250
275
  // So we need to rehydrate it to make it usable
251
- if ((currentState === undefined || !isStateValid(currentState)) && route?.params?.state == null && !(typeof route?.params?.screen === 'string' && route?.params?.initial !== false)) {
276
+ if ((currentState === undefined || !isStateValid(currentState)) && route?.params?.state == null && !(typeof route?.params?.screen === 'string' && route?.params?.initial !== false) && !isNestedParamsConsumed) {
252
277
  return [undefined, router.getInitialState({
253
278
  routeNames,
254
279
  routeParamList: initialRouteParamList,
255
280
  routeGetIdList
256
- }), true];
281
+ }), true, undefined];
257
282
  } else {
258
- const stateFromParams = getStateFromParams(route?.params);
283
+ const paramsForState = isNestedParamsConsumed ? undefined : route?.params;
284
+ const stateFromParams = paramsForState ? getStateFromParams(paramsForState, router.type) : undefined;
259
285
  const stateBeforeInitialization = stateFromParams ?? currentState;
260
- const hydratedState = router.getRehydratedState(stateBeforeInitialization, {
286
+ const hydratedState = stateBeforeInitialization == null ? router.getInitialState({
287
+ routeNames,
288
+ routeParamList: initialRouteParamList,
289
+ routeGetIdList
290
+ }) : router.getRehydratedState(stateBeforeInitialization, {
261
291
  routeNames,
262
292
  routeParamList: initialRouteParamList,
263
293
  routeGetIdList
264
294
  });
265
- if (options.routeNamesChangeBehavior === 'lastUnhandled' && doesStateHaveOnlyInvalidRoutes(stateBeforeInitialization)) {
266
- return [stateBeforeInitialization, hydratedState, true];
295
+ if (stateBeforeInitialization != null && isStateValid(stateBeforeInitialization) && options.routeNamesChangeBehavior === 'lastUnhandled' && doesStateHaveOnlyInvalidRoutes(stateBeforeInitialization)) {
296
+ return [stateBeforeInitialization, hydratedState, true, paramsForState];
267
297
  }
268
- return [undefined, hydratedState, false];
298
+ return [undefined, hydratedState, false,
299
+ // Current state is already initialized
300
+ // So params weren't used, and need to be handled later
301
+ isStateInitialized(currentState) ? undefined : paramsForState];
269
302
  }
270
303
  // We explicitly don't include routeNames, route.params etc. in the dep list
271
304
  // below. We want to avoid forcing a new state to be calculated in those cases
@@ -275,10 +308,17 @@ export function useNavigationBuilder(createRouter, options) {
275
308
  // eslint-disable-next-line react-hooks/exhaustive-deps
276
309
  }, [currentState, router, isStateValid]);
277
310
  const previousRouteKeyListRef = React.useRef(routeKeyList);
278
- React.useEffect(() => {
311
+ React.useInsertionEffect(() => {
279
312
  previousRouteKeyListRef.current = routeKeyList;
280
313
  });
281
314
  const previousRouteKeyList = previousRouteKeyListRef.current;
315
+
316
+ // Names of the screens whose navigation key changed since the last render
317
+ const routeKeyChanges = Object.keys(routeKeyList).filter(name => {
318
+ const current = routeKeyList[name];
319
+ const previous = previousRouteKeyList[name];
320
+ return current != null && previous != null && !isArrayEqual(current, previous);
321
+ });
282
322
  const [unhandledState, setUnhandledState] = React.useState(stateBeforeInitialization);
283
323
 
284
324
  // An unhandled state is state that didn't have any valid routes
@@ -295,57 +335,65 @@ export function useNavigationBuilder(createRouter, options) {
295
335
  isStateInitialized(currentState) ? currentState : initializedState;
296
336
  let nextState = state;
297
337
  let shouldClearUnhandledState = false;
298
-
338
+ if (unhandledState != null && !isStateValid(unhandledState)) {
339
+ // The stored unhandled state is for a different type of navigator
340
+ // It can't be used with this navigator, so we discard it
341
+ setUnhandledState(undefined);
342
+ } else if (
299
343
  // Previously unhandled state is now valid again
300
344
  // And current state no longer has any valid routes
301
345
  // We should reuse the unhandled state instead of re-calculating the state
302
- if (unhandledState?.routes.every(r => routeNames.includes(r.name)) && state?.routes.every(r => !routeNames.includes(r.name))) {
346
+ unhandledState?.routes.every(r => routeNames.includes(r.name)) && state?.routes.every(r => !routeNames.includes(r.name))) {
303
347
  shouldClearUnhandledState = true;
304
348
  nextState = router.getRehydratedState(unhandledState, {
305
349
  routeNames,
306
350
  routeParamList,
307
351
  routeGetIdList
308
352
  });
309
- } else if (!isArrayEqual(state.routeNames, routeNames) || !isRecordEqual(routeKeyList, previousRouteKeyList)) {
353
+ } else if (!isArrayEqual(state.routeNames, routeNames) || routeKeyChanges.length !== 0) {
310
354
  // When the list of route names change, the router should handle it to remove invalid routes
311
355
  nextState = router.getStateForRouteNamesChange(state, {
312
356
  routeNames,
313
357
  routeParamList,
314
358
  routeGetIdList,
315
- routeKeyChanges: Object.keys(routeKeyList).filter(name => name in previousRouteKeyList && routeKeyList[name] !== previousRouteKeyList[name])
359
+ routeKeyChanges
316
360
  });
317
361
  }
318
- const previousNestedParamsRef = React.useRef(route?.params);
319
- React.useEffect(() => {
320
- previousNestedParamsRef.current = route?.params;
321
- }, [route?.params]);
322
- if (route?.params) {
323
- const previousParams = previousNestedParamsRef.current;
362
+ let didConsumeNestedParams = route?.params === paramsUsedForInitialization;
363
+ if (route?.params && !didConsumeNestedParams) {
324
364
  let action;
325
- if (typeof route.params.state === 'object' && route.params.state != null && route.params !== previousParams) {
326
- if (options.routeNamesChangeBehavior === 'lastUnhandled' && doesStateHaveOnlyInvalidRoutes(route.params.state)) {
327
- if (route.params.state !== unhandledState) {
328
- setUnhandledState(route.params.state);
365
+ if (isNavigationState(route.params.state) && !isNestedParamsConsumed) {
366
+ didConsumeNestedParams = true;
367
+ const stateFromParams = getStateFromParams(route.params, router.type);
368
+ if (stateFromParams != null) {
369
+ if (options.routeNamesChangeBehavior === 'lastUnhandled' && doesStateHaveOnlyInvalidRoutes(stateFromParams)) {
370
+ if (stateFromParams !== unhandledState) {
371
+ setUnhandledState(stateFromParams);
372
+ }
373
+ } else {
374
+ // If the route was updated with new state, we should reset to it
375
+ action = CommonActions.reset(stateFromParams);
329
376
  }
330
- } else {
331
- // If the route was updated with new state, we should reset to it
332
- action = CommonActions.reset(route.params.state);
333
377
  }
334
- } else if (typeof route.params.screen === 'string' && (route.params.initial === false && isFirstStateInitialization || route.params !== previousParams)) {
378
+ } else if (typeof route.params.screen === 'string' && (route.params.initial === false && isFirstStateInitialization || !isNestedParamsConsumed)) {
379
+ didConsumeNestedParams = true;
335
380
  if (options.routeNamesChangeBehavior === 'lastUnhandled' && !routeNames.includes(route.params.screen)) {
336
- const state = getStateFromParams(route.params);
381
+ const state = getStateFromParams(route.params, router.type);
337
382
  if (state != null && !deepEqual(state, unhandledState)) {
338
383
  setUnhandledState(state);
339
384
  }
340
385
  } else {
341
386
  // If the route was updated with new screen name and/or params, we should navigate there
342
- action = CommonActions.navigate({
343
- name: route.params.screen,
344
- params: route.params.params,
345
- path: route.params.path,
346
- merge: route.params.merge,
347
- pop: route.params.pop
348
- });
387
+ action = {
388
+ type: 'NAVIGATE',
389
+ payload: {
390
+ name: route.params.screen,
391
+ params: route.params.params,
392
+ path: route.params.path,
393
+ merge: route.params.merge,
394
+ pop: route.params.pop
395
+ }
396
+ };
349
397
  }
350
398
  }
351
399
 
@@ -355,16 +403,25 @@ export function useNavigationBuilder(createRouter, options) {
355
403
  routeParamList,
356
404
  routeGetIdList
357
405
  }) : null;
406
+ if (process.env.NODE_ENV !== 'production' && action != null && updatedState === null) {
407
+ const names = routeNames.map(name => `'${name}'`).join(', ');
408
+ console.error(`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.`);
409
+ }
358
410
  nextState = updatedState !== null ? router.getRehydratedState(updatedState, {
359
411
  routeNames,
360
412
  routeParamList,
361
413
  routeGetIdList
362
414
  }) : nextState;
363
415
  }
364
- const shouldUpdate = state !== nextState || typeof route?.params?.state === 'object' || typeof route?.params?.screen === 'string';
416
+ React.useEffect(() => {
417
+ if (consumedParams && didConsumeNestedParams && typeof route?.params === 'object' && route.params != null) {
418
+ consumedParams.set(route.params, true);
419
+ }
420
+ }, [consumedParams, didConsumeNestedParams, route?.params]);
421
+ const shouldUpdate = state !== nextState;
365
422
  useScheduleUpdate(() => {
366
423
  if (shouldUpdate) {
367
- // If the state needs to be updated, we'll schedule an update
424
+ // Schedule an update if the state needs to be updated
368
425
  setState(nextState);
369
426
  if (shouldClearUnhandledState) {
370
427
  setUnhandledState(undefined);
@@ -376,44 +433,54 @@ export function useNavigationBuilder(createRouter, options) {
376
433
  // We can't use the outdated state since the screens have changed, which will cause error due to mismatched config
377
434
  // So we override the state object we return to use the latest state as soon as possible
378
435
  state = nextState;
436
+
437
+ // Last state to reuse if component gets cleaned up due to `<Activity mode="hidden">`
438
+ React.useEffect(() => {
439
+ lastStateRef.current = state;
440
+ });
441
+ const lastNotifiedStateRef = React.useRef(null);
379
442
  React.useEffect(() => {
380
443
  // In strict mode, React will double-invoke effects.
381
444
  // So we need to reset the flag if component was not unmounted
382
- stateCleanedUp.current = false;
445
+ stateCleanupRef.current = false;
383
446
  setKey(navigatorKey);
384
- if (!getIsInitial()) {
385
- // If it's not initial render, we need to update the state
386
- // This will make sure that our container gets notifier of state changes due to new mounts
447
+ if ((!getIsInitial() || getIsStateEmitted()) && lastNotifiedStateRef.current !== state) {
448
+ // We need to notify the state update in these scenarios:
449
+ // 1. If it's not the initial render of the component containing the navigator
450
+ // 2. If the container has already emitted state before the navigator mounted
451
+ //
452
+ // This will make sure that the container gets notified of state changes due to new mounts
387
453
  // This is necessary for proper screen tracking, URL updates etc.
388
- setState(nextState);
454
+ //
455
+ // During hydration, a nested navigator may mount after the container's initial state event.
456
+ // Or mount could be delayed due to conditional rendering, suspense etc.
457
+ // So only checking for the initial render is not enough.
458
+ // We also need to propagate the navigator's initial state when it mounts later,
459
+ // so the container is notified that the nested state is now available.
460
+ //
461
+ // We only notify if the state is different from what we already notified
462
+ // Otherwise, this goes into a loop when inside `<Activity mode="hidden">`
463
+ setState(state);
464
+ lastNotifiedStateRef.current = state;
389
465
  }
390
466
  return () => {
391
467
  // We need to clean up state for this navigator on unmount
392
468
  if (getCurrentState() !== undefined && getKey() === navigatorKey) {
393
469
  setCurrentState(undefined);
394
- stateCleanedUp.current = true;
470
+ stateCleanupRef.current = true;
395
471
  }
472
+
473
+ // Reset so that StrictMode's second mount re-propagates state correctly.
474
+ // Without this, the guard above sees the same reference and skips setState,
475
+ // causing the initial state to be lost after cleanup wipes the container state.
476
+ lastNotifiedStateRef.current = null;
396
477
  };
397
478
  // eslint-disable-next-line react-hooks/exhaustive-deps
398
479
  }, []);
399
-
400
- // In some cases (e.g. route names change), internal state might have changed
401
- // But it hasn't been committed yet, so hasn't propagated to the sync external store
402
- // During this time, we need to return the internal state in `getState`
403
- // Otherwise it can result in inconsistent state during render in children
404
- // To avoid this, we use a ref for render phase, and immediately clear it on commit
405
- const stateRef = React.useRef(state);
406
- stateRef.current = state;
407
- React.useLayoutEffect(() => {
408
- stateRef.current = null;
409
- });
410
480
  const getState = useLatestCallback(() => {
411
481
  const currentState = getCurrentState();
412
482
  return deepFreeze(isStateInitialized(currentState) ? currentState : initializedState);
413
483
  });
414
- const {
415
- onEmitEvent
416
- } = React.useContext(NavigationBuilderContext);
417
484
  const emitter = useEventEmitter(e => {
418
485
  const routeNames = [];
419
486
  let route;
@@ -429,32 +496,33 @@ export function useNavigationBuilder(createRouter, options) {
429
496
  if (route == null) {
430
497
  return;
431
498
  }
432
- const navigation = descriptors[route.key].navigation;
433
- const listeners = [].concat(
434
- // Get an array of listeners for all screens + common listeners on navigator
435
- ...[screenListeners, ...routeNames.map(name => {
436
- const {
437
- listeners
438
- } = screens[name].props;
439
- return listeners;
440
- })].map(listeners => {
441
- const map = typeof listeners === 'function' ? listeners({
442
- route: route,
443
- navigation
444
- }) : listeners;
445
- return map ? Object.keys(map).filter(type => type === e.type).map(type => map?.[type]) : undefined;
446
- }))
447
- // We don't want same listener to be called multiple times for same event
448
- // So we remove any duplicate functions from the array
449
- .filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
450
- listeners.forEach(listener => listener?.(e));
451
- onEmitEvent({
452
- type: e.type,
453
- data: e.data,
454
- target: e.target,
455
- defaultPrevented: e.defaultPrevented
456
- });
457
- });
499
+ const hasPerScreenListeners = routeNames.some(name => screens[name]?.props.listeners != null);
500
+ if (screenListeners != null || hasPerScreenListeners) {
501
+ const descriptor = descriptors[route.key];
502
+ if (descriptor == null) {
503
+ throw new Error(`Couldn't find a descriptor for route '${route.key}'.`);
504
+ }
505
+ const navigation = descriptor.navigation;
506
+ const listeners = [].concat(
507
+ // Get an array of listeners for all screens + common listeners on navigator
508
+ ...[screenListeners, ...routeNames.map(name => {
509
+ const {
510
+ listeners
511
+ } = screens[name]?.props ?? {};
512
+ return listeners;
513
+ })].map(listeners => {
514
+ const map = typeof listeners === 'function' ? listeners({
515
+ route: route,
516
+ navigation
517
+ }) : listeners;
518
+ return map ? Object.keys(map).filter(type => type === e.type).map(type => map?.[type]) : undefined;
519
+ }))
520
+ // We don't want same listener to be called multiple times for same event
521
+ // So we remove any duplicate functions from the array
522
+ .filter((cb, i, self) => cb && self.lastIndexOf(cb) === i);
523
+ listeners.forEach(listener => listener?.(e));
524
+ }
525
+ }, onEmitEvent);
458
526
  useFocusEvents({
459
527
  state,
460
528
  emitter
@@ -495,7 +563,7 @@ export function useNavigationBuilder(createRouter, options) {
495
563
  getState,
496
564
  setState
497
565
  });
498
- const onUnhandledActionParent = React.useContext(UnhandledActionContext);
566
+ const onUnhandledActionParent = React.use(UnhandledActionContext);
499
567
  const onUnhandledAction = useLatestCallback(action => {
500
568
  if (options.routeNamesChangeBehavior === 'lastUnhandled' && action.type === 'NAVIGATE' && action.payload != null && 'name' in action.payload && typeof action.payload.name === 'string' && !routeNames.includes(action.payload.name)) {
501
569
  const state = {
@@ -513,9 +581,9 @@ export function useNavigationBuilder(createRouter, options) {
513
581
  onAction,
514
582
  onUnhandledAction,
515
583
  getState,
584
+ state,
516
585
  emitter,
517
- router,
518
- stateRef
586
+ router
519
587
  });
520
588
  useFocusedListenersChildrenAdapter({
521
589
  navigation,
@@ -526,7 +594,7 @@ export function useNavigationBuilder(createRouter, options) {
526
594
  getStateListeners: keyedListeners.getState
527
595
  });
528
596
  const descriptors = useDescriptors({
529
- routes: router.getRoutesFromState ? router.getRoutesFromState(state) : state.routes,
597
+ routes: state.routes,
530
598
  screens,
531
599
  navigation,
532
600
  screenOptions,
@@ -534,6 +602,7 @@ export function useNavigationBuilder(createRouter, options) {
534
602
  onAction,
535
603
  getState,
536
604
  setState,
605
+ subscribe,
537
606
  onRouteFocus,
538
607
  addListener,
539
608
  addKeyedListener,
@@ -541,12 +610,11 @@ export function useNavigationBuilder(createRouter, options) {
541
610
  // @ts-expect-error: this should have both core and custom events, but too much work right now
542
611
  emitter
543
612
  });
544
- useCurrentRender({
545
- state,
546
- navigation,
547
- descriptors
548
- });
549
613
  const NavigationContent = useComponent(children => {
614
+ const focusedRoute = state.routes[state.index];
615
+ if (focusedRoute == null) {
616
+ throw new Error(`Couldn't find a route at index ${state.index}.`);
617
+ }
550
618
  const element = layout != null ? layout({
551
619
  state,
552
620
  descriptors,
@@ -559,8 +627,13 @@ export function useNavigationBuilder(createRouter, options) {
559
627
  value: navigation,
560
628
  children: /*#__PURE__*/_jsx(NavigationStateListenerProvider, {
561
629
  state: state,
562
- children: /*#__PURE__*/_jsx(PreventRemoveProvider, {
563
- children: element
630
+ getState: navigation.getState,
631
+ subscribe: subscribe,
632
+ children: /*#__PURE__*/_jsx(FocusedRouteKeyContext.Provider, {
633
+ value: focusedRoute.key,
634
+ children: /*#__PURE__*/_jsx(PreventRemoveProvider, {
635
+ children: element
636
+ })
564
637
  })
565
638
  })
566
639
  })