@react-navigation/core 8.0.0-alpha.4 → 8.0.0-alpha.40

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