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

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 +173 -90
  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 +37 -12
  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 +29 -57
  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 +35 -27
  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 +3 -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 +4 -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 +459 -332
  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 +59 -21
  248. package/src/NavigationRootContext.tsx +11 -0
  249. package/src/NavigationStateContext.tsx +6 -6
  250. package/src/PreventRemoveContext.tsx +1 -0
  251. package/src/PreventRemoveProvider.tsx +55 -56
  252. package/src/SceneView.tsx +31 -57
  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 +44 -30
  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
@@ -1,12 +1,15 @@
1
1
  import type {
2
2
  NavigationAction,
3
+ NavigationRoute,
3
4
  NavigationState,
5
+ ParamListBase,
6
+ PartialRoute,
4
7
  } from '@react-navigation/routers';
5
8
  import * as React from 'react';
6
9
 
7
10
  import {
8
11
  type ChildBeforeRemoveListener,
9
- NavigationBuilderContext,
12
+ useNavigationBuilderContext,
10
13
  } from './NavigationBuilderContext';
11
14
  import { NavigationRouteContext } from './NavigationProvider';
12
15
  import type { EventMapCore } from './types';
@@ -23,50 +26,76 @@ const VISITED_ROUTE_KEYS = Symbol('VISITED_ROUTE_KEYS');
23
26
  export const shouldPreventRemove = (
24
27
  emitter: NavigationEventEmitter<EventMapCore<any>>,
25
28
  beforeRemoveListeners: Record<string, ChildBeforeRemoveListener | undefined>,
26
- currentRoutes: { key: string }[],
27
- nextRoutes: { key?: string | undefined }[],
29
+ currentRoutes: NavigationRoute<ParamListBase, string>[],
30
+ nextRoutes: (
31
+ | NavigationRoute<ParamListBase, string>
32
+ | PartialRoute<NavigationRoute<ParamListBase, string>>
33
+ )[],
28
34
  action: NavigationAction
29
35
  ) => {
30
- const nextRouteKeys = nextRoutes.map((route) => route.key);
36
+ const nextRoutesByKey = new Map(
37
+ nextRoutes
38
+ .map((route) => [route.key, route] as const)
39
+ .filter(([, route]) => route.key != null)
40
+ );
31
41
 
32
- // Call these in reverse order so last screens handle the event first
33
- const removedRoutes = currentRoutes
34
- .filter((route) => !nextRouteKeys.includes(route.key))
35
- .reverse();
36
-
37
- const visitedRouteKeys: Set<string> =
38
- // @ts-expect-error: add this property to mark that we've already emitted this action
39
- action[VISITED_ROUTE_KEYS] ?? new Set<string>();
42
+ const visitedRouteKeys =
43
+ VISITED_ROUTE_KEYS in action && action[VISITED_ROUTE_KEYS] instanceof Set
44
+ ? action[VISITED_ROUTE_KEYS]
45
+ : new Set<string>();
40
46
 
41
47
  const beforeRemoveAction = {
42
48
  ...action,
43
49
  [VISITED_ROUTE_KEYS]: visitedRouteKeys,
44
50
  };
45
51
 
46
- for (const route of removedRoutes) {
52
+ // Call these in reverse order so last screens handle the event first
53
+ const reversedCurrentRoutes = [...currentRoutes].reverse();
54
+
55
+ for (const route of reversedCurrentRoutes) {
47
56
  if (visitedRouteKeys.has(route.key)) {
48
57
  // Skip if we've already emitted this action for this screen
49
58
  continue;
50
59
  }
51
60
 
52
- // First, we need to check if any child screens want to prevent it
53
- const isPrevented = beforeRemoveListeners[route.key]?.(beforeRemoveAction);
54
-
55
- if (isPrevented) {
56
- return true;
57
- }
58
-
59
- visitedRouteKeys.add(route.key);
61
+ if (!nextRoutesByKey.has(route.key)) {
62
+ // The route is not in next state, so it's being removed
63
+ // First, we need to check if any child screens want to prevent it
64
+ const isPrevented = beforeRemoveListeners[route.key]?.(
65
+ beforeRemoveAction,
66
+ undefined
67
+ );
68
+
69
+ if (isPrevented) {
70
+ return true;
71
+ }
72
+
73
+ visitedRouteKeys.add(route.key);
74
+
75
+ const event = emitter.emit({
76
+ type: 'beforeRemove',
77
+ target: route.key,
78
+ data: { action: beforeRemoveAction },
79
+ canPreventDefault: true,
80
+ });
60
81
 
61
- const event = emitter.emit({
62
- type: 'beforeRemove',
63
- target: route.key,
64
- data: { action: beforeRemoveAction },
65
- canPreventDefault: true,
66
- });
82
+ if (event.defaultPrevented) {
83
+ return true;
84
+ }
85
+ } else {
86
+ // The route is kept but its nested state changed, so propagate the check into the nested navigator
87
+ const nextRoute = nextRoutesByKey.get(route.key);
88
+
89
+ if (route.state != null && route.state !== nextRoute?.state) {
90
+ const isPrevented = beforeRemoveListeners[route.key]?.(
91
+ beforeRemoveAction,
92
+ nextRoute?.state
93
+ );
67
94
 
68
- if (event.defaultPrevented) {
69
- return true;
95
+ if (isPrevented) {
96
+ return true;
97
+ }
98
+ }
70
99
  }
71
100
  }
72
101
 
@@ -78,23 +107,29 @@ export function useOnPreventRemove({
78
107
  emitter,
79
108
  beforeRemoveListeners,
80
109
  }: Options) {
81
- const { addKeyedListener } = React.useContext(NavigationBuilderContext);
82
- const route = React.useContext(NavigationRouteContext);
110
+ const { addKeyedListener } = useNavigationBuilderContext();
111
+
112
+ const route = React.use(NavigationRouteContext);
113
+
83
114
  const routeKey = route?.key;
84
115
 
85
- React.useEffect(() => {
116
+ React.useInsertionEffect(() => {
86
117
  if (routeKey) {
87
- return addKeyedListener?.('beforeRemove', routeKey, (action) => {
88
- const state = getState();
89
-
90
- return shouldPreventRemove(
91
- emitter,
92
- beforeRemoveListeners,
93
- state.routes,
94
- [],
95
- action
96
- );
97
- });
118
+ return addKeyedListener?.(
119
+ 'beforeRemove',
120
+ routeKey,
121
+ (action, nextState) => {
122
+ const state = getState();
123
+
124
+ return shouldPreventRemove(
125
+ emitter,
126
+ beforeRemoveListeners,
127
+ state.routes,
128
+ nextState?.routes ?? [],
129
+ action
130
+ );
131
+ }
132
+ );
98
133
  }
99
134
  }, [addKeyedListener, beforeRemoveListeners, emitter, getState, routeKey]);
100
135
  }
@@ -5,13 +5,13 @@ import type {
5
5
  } from '@react-navigation/routers';
6
6
  import * as React from 'react';
7
7
 
8
- import { NavigationBuilderContext } from './NavigationBuilderContext';
8
+ import { useNavigationBuilderContext } from './NavigationBuilderContext';
9
9
 
10
- type Options<Action extends NavigationAction> = {
11
- router: Router<NavigationState, Action>;
12
- getState: () => NavigationState;
13
- setState: (state: NavigationState) => void;
14
- key?: string;
10
+ type Options<State extends NavigationState, Action extends NavigationAction> = {
11
+ router: Router<State, Action>;
12
+ getState: () => State;
13
+ setState: (state: State) => void;
14
+ key?: string | undefined;
15
15
  };
16
16
 
17
17
  /**
@@ -19,15 +19,11 @@ type Options<Action extends NavigationAction> = {
19
19
  * Focus action needs to be treated specially, coz when a nested route is focused,
20
20
  * the parent navigators also needs to be focused.
21
21
  */
22
- export function useOnRouteFocus<Action extends NavigationAction>({
23
- router,
24
- getState,
25
- key: sourceRouteKey,
26
- setState,
27
- }: Options<Action>) {
28
- const { onRouteFocus: onRouteFocusParent } = React.useContext(
29
- NavigationBuilderContext
30
- );
22
+ export function useOnRouteFocus<
23
+ State extends NavigationState,
24
+ Action extends NavigationAction,
25
+ >({ router, getState, key: sourceRouteKey, setState }: Options<State, Action>) {
26
+ const { onRouteFocus: onRouteFocusParent } = useNavigationBuilderContext();
31
27
 
32
28
  return React.useCallback(
33
29
  (key: string) => {
@@ -1,42 +1,41 @@
1
- import type { ParamListBase } from '@react-navigation/routers';
2
1
  import * as React from 'react';
3
2
 
4
3
  import { NavigationBuilderContext } from './NavigationBuilderContext';
5
- import { NavigationStateContext } from './NavigationStateContext';
6
- import type { NavigationProp } from './types';
4
+ import { IsFocusedContext } from './useIsFocused';
5
+
6
+ export const OptionsGetterContext = React.createContext<
7
+ ((key: string, getter: () => object | undefined | null) => void) | undefined
8
+ >(undefined);
7
9
 
8
10
  type Options = {
9
11
  key?: string;
10
- navigation?: NavigationProp<ParamListBase>;
11
12
  options?: object | undefined;
13
+ getFocusedRouteKey?: () => string | undefined;
12
14
  };
13
15
 
14
- export function useOptionsGetters({ key, options, navigation }: Options) {
16
+ export function useOptionsGetters({
17
+ key,
18
+ options,
19
+ getFocusedRouteKey,
20
+ }: Options) {
21
+ const isFocused = React.use(IsFocusedContext) ?? true;
22
+
15
23
  const optionsRef = React.useRef<object | undefined>(options);
16
24
  const optionsGettersFromChildRef = React.useRef<
17
25
  Record<string, () => object | undefined | null>
18
26
  >({});
19
27
 
20
- const { onOptionsChange } = React.useContext(NavigationBuilderContext);
21
- const { addOptionsGetter: parentAddOptionsGetter } = React.useContext(
22
- NavigationStateContext
23
- );
28
+ const onOptionsChange = React.use(NavigationBuilderContext)?.onOptionsChange;
24
29
 
25
- const optionsChangeListener = React.useCallback(() => {
26
- const isFocused = navigation?.isFocused() ?? true;
27
- const hasChildren = Object.keys(optionsGettersFromChildRef.current).length;
30
+ const parentAddOptionsGetter = React.use(OptionsGetterContext);
28
31
 
29
- if (isFocused && !hasChildren) {
30
- onOptionsChange(optionsRef.current ?? {});
31
- }
32
- }, [navigation, onOptionsChange]);
33
-
34
- React.useEffect(() => {
32
+ React.useInsertionEffect(() => {
35
33
  optionsRef.current = options;
36
- optionsChangeListener();
34
+ }, [options]);
37
35
 
38
- return navigation?.addListener('focus', optionsChangeListener);
39
- }, [navigation, options, optionsChangeListener]);
36
+ React.useEffect(() => {
37
+ onOptionsChange?.();
38
+ }, [isFocused, onOptionsChange, options]);
40
39
 
41
40
  const getOptionsFromListener = React.useCallback(() => {
42
41
  for (const key in optionsGettersFromChildRef.current) {
@@ -54,9 +53,11 @@ export function useOptionsGetters({ key, options, navigation }: Options) {
54
53
  }, []);
55
54
 
56
55
  const getCurrentOptions = React.useCallback(() => {
57
- const isFocused = navigation?.isFocused() ?? true;
58
-
59
- if (!isFocused) {
56
+ // We use focused route key from the navigator instead of `isFocused`
57
+ // Because `isFocused` may reflect the value the screen last rendered with,
58
+ // e.g. if `Activity` hides the screen before it rendered the new value,
59
+ // it updates at a lower priority, so it may be delayed.
60
+ if (key != null && getFocusedRouteKey?.() !== key) {
60
61
  return null;
61
62
  }
62
63
 
@@ -67,24 +68,37 @@ export function useOptionsGetters({ key, options, navigation }: Options) {
67
68
  }
68
69
 
69
70
  return optionsRef.current;
70
- }, [navigation, getOptionsFromListener]);
71
+ }, [key, getFocusedRouteKey, getOptionsFromListener]);
71
72
 
72
- React.useEffect(() => {
73
- return parentAddOptionsGetter?.(key!, getCurrentOptions);
73
+ React.useInsertionEffect(() => {
74
+ // We don't have a parent at the root
75
+ if (key != null) {
76
+ return parentAddOptionsGetter?.(key, getCurrentOptions);
77
+ }
74
78
  }, [getCurrentOptions, parentAddOptionsGetter, key]);
75
79
 
76
80
  const addOptionsGetter = React.useCallback(
77
81
  (key: string, getter: () => object | undefined | null) => {
78
82
  optionsGettersFromChildRef.current[key] = getter;
79
- optionsChangeListener();
83
+
84
+ // Hidden screens may register without running their passive effects.
85
+ // Wait until the commit finishes before reporting their options.
86
+ if (onOptionsChange !== undefined) {
87
+ void Promise.resolve().then(onOptionsChange);
88
+ }
80
89
 
81
90
  return () => {
82
91
  // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
83
92
  delete optionsGettersFromChildRef.current[key];
84
- optionsChangeListener();
93
+
94
+ // Other getters and the focused route may still change during cleanup.
95
+ // Wait until the commit finishes before reporting the remaining options.
96
+ if (onOptionsChange !== undefined) {
97
+ void Promise.resolve().then(onOptionsChange);
98
+ }
85
99
  };
86
100
  },
87
- [optionsChangeListener]
101
+ [onOptionsChange]
88
102
  );
89
103
 
90
104
  return {
@@ -1,5 +1,4 @@
1
1
  import type { NavigationAction } from '@react-navigation/routers';
2
- import { nanoid } from 'nanoid/non-secure';
3
2
  import * as React from 'react';
4
3
  import useLatestCallback from 'use-latest-callback';
5
4
 
@@ -18,20 +17,29 @@ export function usePreventRemove(
18
17
  preventRemove: boolean,
19
18
  callback: (options: { data: { action: NavigationAction } }) => void
20
19
  ) {
21
- const [id] = React.useState(() => nanoid());
20
+ const id = React.useId();
22
21
 
23
22
  const navigation = useNavigation();
24
23
  const { key: routeKey } = useRoute();
25
24
 
26
- const { setPreventRemove } = usePreventRemoveContext();
25
+ const { setPreventRemove, notifyPreventRemove } = usePreventRemoveContext();
27
26
 
28
- React.useEffect(() => {
27
+ React.useInsertionEffect(() => {
29
28
  setPreventRemove(id, routeKey, preventRemove);
29
+
30
30
  return () => {
31
31
  setPreventRemove(id, routeKey, false);
32
32
  };
33
33
  }, [setPreventRemove, id, routeKey, preventRemove]);
34
34
 
35
+ React.useEffect(() => {
36
+ notifyPreventRemove();
37
+
38
+ return () => {
39
+ notifyPreventRemove();
40
+ };
41
+ }, [id, routeKey, preventRemove, notifyPreventRemove]);
42
+
35
43
  const beforeRemoveListener = useLatestCallback<
36
44
  EventListenerCallback<EventMapCore<any>, 'beforeRemove'>
37
45
  >((e) => {
@@ -44,7 +52,7 @@ export function usePreventRemove(
44
52
  callback({ data: e.data });
45
53
  });
46
54
 
47
- React.useEffect(
55
+ React.useInsertionEffect(
48
56
  () => navigation?.addListener('beforeRemove', beforeRemoveListener),
49
57
  [navigation, beforeRemoveListener]
50
58
  );
@@ -3,11 +3,11 @@ import * as React from 'react';
3
3
  import { PreventRemoveContext } from './PreventRemoveContext';
4
4
 
5
5
  export function usePreventRemoveContext() {
6
- const value = React.useContext(PreventRemoveContext);
6
+ const value = React.use(PreventRemoveContext);
7
7
 
8
8
  if (value == null) {
9
9
  throw new Error(
10
- "Couldn't find the prevent remove context. Is your component inside NavigationContent?"
10
+ "Couldn't find the prevent remove context. This is likely a bug in the navigator.\n\nIf you're using a custom navigator, make sure that the navigator content is wrapped by the 'render' function returned by 'useNavigationBuilder'."
11
11
  );
12
12
  }
13
13
 
@@ -1,4 +1,3 @@
1
- import { nanoid } from 'nanoid/non-secure';
2
1
  import * as React from 'react';
3
2
 
4
3
  import { SingleNavigatorContext } from './EnsureSingleNavigator';
@@ -8,8 +7,9 @@ import { SingleNavigatorContext } from './EnsureSingleNavigator';
8
7
  * This is used to prevent multiple navigators under a single container or screen.
9
8
  */
10
9
  export function useRegisterNavigator() {
11
- const [key] = React.useState(() => nanoid());
12
- const container = React.useContext(SingleNavigatorContext);
10
+ const key = React.useId();
11
+
12
+ const container = React.use(SingleNavigatorContext);
13
13
 
14
14
  if (container === undefined) {
15
15
  throw new Error(
@@ -17,7 +17,7 @@ export function useRegisterNavigator() {
17
17
  );
18
18
  }
19
19
 
20
- React.useEffect(() => {
20
+ React.useInsertionEffect(() => {
21
21
  const { register, unregister } = container;
22
22
 
23
23
  register(key);
package/src/useRoute.tsx CHANGED
@@ -5,15 +5,26 @@ import {
5
5
  NamedRouteContextListContext,
6
6
  NavigationRouteContext,
7
7
  } from './NavigationProvider';
8
- import type { RootParamList, RouteForName, RouteProp } from './types';
8
+ import type {
9
+ NestedParamLists,
10
+ RootParamList,
11
+ RouteForName,
12
+ RouteProp,
13
+ } from './types';
14
+ import type { KeyOf } from './utilities';
9
15
 
10
16
  /**
11
17
  * Get all possible route names from a param list and its nested navigators.
18
+ * Recurses into the param lists directly instead of using `RouteForName`,
19
+ * So the route objects don't need to be built just to get the names.
12
20
  */
13
- type AllRouteNames<ParamList extends {}> = RouteForName<
14
- ParamList,
15
- string
16
- >['name'];
21
+ type AllRouteNames<ParamList extends {}> =
22
+ | KeyOf<ParamList>
23
+ | AllNestedRouteNames<NestedParamLists<ParamList>>;
24
+
25
+ type AllNestedRouteNames<ParamLists> = ParamLists extends {}
26
+ ? AllRouteNames<ParamLists>
27
+ : never;
17
28
 
18
29
  /**
19
30
  * Hook to access the route prop of the parent screen anywhere.
@@ -22,7 +33,10 @@ type AllRouteNames<ParamList extends {}> = RouteForName<
22
33
  */
23
34
  export function useRoute<
24
35
  const ParamList extends {} = RootParamList,
25
- const RouteName extends AllRouteNames<ParamList> = AllRouteNames<ParamList>,
36
+ // The `& string` reduces the constraint to a plain union of names,
37
+ // so errors for invalid names list the valid names
38
+ const RouteName extends AllRouteNames<ParamList> & string =
39
+ AllRouteNames<ParamList> & string,
26
40
  >(name: RouteName): RouteForName<ParamList, RouteName>;
27
41
  export function useRoute<
28
42
  const ParamList extends {} = RootParamList,
@@ -4,8 +4,6 @@ import * as React from 'react';
4
4
  import { isRecordEqual } from './isRecordEqual';
5
5
  import type { RouteProp } from './types';
6
6
 
7
- type RouteCache = Map<string, RouteProp<ParamListBase>>;
8
-
9
7
  /**
10
8
  * Utilities such as `getFocusedRouteNameFromRoute` need to access state.
11
9
  * So we need a way to suppress the warning for those use cases.
@@ -13,6 +11,10 @@ type RouteCache = Map<string, RouteProp<ParamListBase>>;
13
11
  */
14
12
  export const CHILD_STATE = Symbol('CHILD_STATE');
15
13
 
14
+ type CachedRoute = RouteProp<ParamListBase> & {
15
+ [CHILD_STATE]?: NavigationState;
16
+ };
17
+
16
18
  /**
17
19
  * Hook to cache route props for each screen in the navigator.
18
20
  * This lets add warnings and modifications to the route object but keep references between renders.
@@ -21,47 +23,67 @@ export function useRouteCache<State extends NavigationState>(
21
23
  routes: State['routes']
22
24
  ) {
23
25
  // Cache object which holds route objects for each screen
24
- const cache = React.useMemo(() => ({ current: new Map() as RouteCache }), []);
26
+ const cache = React.useMemo(
27
+ () => ({ current: new Map<string, CachedRoute>() }),
28
+ []
29
+ );
25
30
 
26
- cache.current = routes.reduce((acc, route) => {
27
- const previous = cache.current.get(route.key);
28
- const { state, ...routeWithoutState } = route;
31
+ const next = React.useMemo(() => {
32
+ return routes.reduce((acc, route) => {
33
+ const previous = cache.current.get(route.key);
29
34
 
30
- let proxy;
35
+ const { state: _, ...routeWithoutState } = route;
31
36
 
32
- if (previous && isRecordEqual(previous, routeWithoutState)) {
33
- // If a cached route object already exists, reuse it
34
- proxy = previous;
35
- } else {
36
- proxy = routeWithoutState;
37
- }
37
+ let proxy: CachedRoute;
38
38
 
39
- if (process.env.NODE_ENV !== 'production') {
40
- // FIXME: since the state is updated with mutation, the route object cannot be frozen
41
- // As a workaround, loop through the object and make the properties readonly
42
- for (const key in proxy) {
43
- // @ts-expect-error: this is fine since we are looping through the object
44
- const value = proxy[key];
39
+ if (previous && isRecordEqual(previous, routeWithoutState)) {
40
+ // If a cached route object already exists, reuse it
41
+ proxy = previous;
42
+ } else {
43
+ proxy = routeWithoutState;
45
44
 
46
- Object.defineProperty(proxy, key, {
47
- enumerable: true,
48
- configurable: true,
49
- writable: false,
50
- value,
51
- });
45
+ if (process.env.NODE_ENV !== 'production') {
46
+ // FIXME: since the state is updated with mutation, the route object cannot be frozen
47
+ // As a workaround, loop through the object and make the properties readonly
48
+ // Only needed once per proxy - skip if we're reusing a previously-frozen one
49
+ for (const [key, value] of Object.entries(proxy)) {
50
+ Object.defineProperty(proxy, key, {
51
+ enumerable: true,
52
+ configurable: true,
53
+ writable: false,
54
+ value,
55
+ });
56
+ }
57
+ }
52
58
  }
53
- }
54
59
 
55
- Object.defineProperty(proxy, CHILD_STATE, {
56
- enumerable: false,
57
- configurable: true,
58
- value: state,
59
- });
60
+ acc.set(route.key, proxy);
60
61
 
61
- acc.set(route.key, proxy);
62
+ return acc;
63
+ }, new Map<string, CachedRoute>());
64
+ }, [cache, routes]);
62
65
 
63
- return acc;
64
- }, new Map() as RouteCache);
66
+ React.useInsertionEffect(() => {
67
+ cache.current = next;
68
+ });
69
+
70
+ // Update child state on every render, including when the memo is reused.
71
+ // This restores the previous child state when a pending render is canceled.
72
+ return Array.from(next.values(), (proxy, index) => {
73
+ const state = routes[index]?.state;
74
+
75
+ // FIXME: We mutate the child state without replacing the cached route object,
76
+ // so nested navigation doesn't re-render the parent screen.
77
+ // This is not concurrent render safe, pending renders mutate this shared child state.
78
+ // Then `getFocusedRouteNameFromRoute` can expose it before the render commits.
79
+ if (proxy[CHILD_STATE] !== state) {
80
+ Object.defineProperty(proxy, CHILD_STATE, {
81
+ enumerable: false,
82
+ configurable: true,
83
+ value: state,
84
+ });
85
+ }
65
86
 
66
- return Array.from(cache.current.values());
87
+ return proxy;
88
+ });
67
89
  }
@@ -1,22 +1,24 @@
1
1
  import * as React from 'react';
2
+ import useLatestCallback from 'use-latest-callback';
2
3
 
3
- import { NavigationBuilderContext } from './NavigationBuilderContext';
4
+ import { useNavigationBuilderContext } from './NavigationBuilderContext';
4
5
 
5
6
  /**
6
7
  * When screen config changes, we want to update the navigator in the same update phase.
7
- * However, navigation state is in the root component and React won't let us update it from a child.
8
- * This is a workaround for that, the scheduled update is stored in the ref without actually calling setState.
9
- * It lets all subsequent updates access the latest state so it stays correct.
10
- * Then we call setState during after the component updates.
8
+ * However, navigation state is in the root component and React won't let us update it from a child during render.
9
+ * The render could also be interrupted in concurrent mode, so we need to update after commit.
10
+ * So we queue the update in an insertion effect and flush it in a layout effect.
11
+ * Insertion effects run before layout effects, so every navigator in the tree has queued its update before we flush.
12
+ * This lets nested updates be applied from the root down without clobbering each other.
11
13
  */
12
14
  export function useScheduleUpdate(callback: () => void) {
13
- const { scheduleUpdate, flushUpdates } = React.useContext(
14
- NavigationBuilderContext
15
- );
15
+ const { scheduleUpdate, flushUpdates } = useNavigationBuilderContext();
16
16
 
17
- // FIXME: This is potentially unsafe
18
- // However, since we are using sync store, it might be fine
19
- scheduleUpdate(callback);
17
+ const latestCallback = useLatestCallback(callback);
18
+
19
+ React.useInsertionEffect(() => {
20
+ scheduleUpdate(latestCallback);
21
+ });
20
22
 
21
23
  React.useLayoutEffect(flushUpdates);
22
24
  }
@@ -9,7 +9,7 @@ import { NavigationFocusedRouteStateContext } from './NavigationFocusedRouteStat
9
9
  * @returns Minimal state to build a path for the current route.
10
10
  */
11
11
  export function useStateForPath() {
12
- const state = React.useContext(NavigationFocusedRouteStateContext);
12
+ const state = React.use(NavigationFocusedRouteStateContext);
13
13
 
14
14
  return state;
15
15
  }