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

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 (283) hide show
  1. package/lib/module/BaseNavigationContainer.js +34 -17
  2. package/lib/module/BaseNavigationContainer.js.map +1 -1
  3. package/lib/module/ConsumedParamsContext.js +5 -0
  4. package/lib/module/ConsumedParamsContext.js.map +1 -0
  5. package/lib/module/DataLoading.js +127 -0
  6. package/lib/module/DataLoading.js.map +1 -0
  7. package/lib/module/NavigationBuilderContext.js +1 -0
  8. package/lib/module/NavigationBuilderContext.js.map +1 -1
  9. package/lib/module/NavigationIndependentTree.js +17 -8
  10. package/lib/module/NavigationIndependentTree.js.map +1 -1
  11. package/lib/module/NavigationProvider.js +22 -11
  12. package/lib/module/NavigationProvider.js.map +1 -1
  13. package/lib/module/NavigationStateContext.js.map +1 -1
  14. package/lib/module/PreventRemoveContext.js.map +1 -1
  15. package/lib/module/PreventRemoveProvider.js +37 -27
  16. package/lib/module/PreventRemoveProvider.js.map +1 -1
  17. package/lib/module/SceneView.js +7 -40
  18. package/lib/module/SceneView.js.map +1 -1
  19. package/lib/module/StaticNavigation.js +179 -50
  20. package/lib/module/StaticNavigation.js.map +1 -1
  21. package/lib/module/StaticTreeContext.js +5 -0
  22. package/lib/module/StaticTreeContext.js.map +1 -0
  23. package/lib/module/checkSerializable.js +18 -13
  24. package/lib/module/checkSerializable.js.map +1 -1
  25. package/lib/module/createNavigationContainerRef.js +6 -8
  26. package/lib/module/createNavigationContainerRef.js.map +1 -1
  27. package/lib/module/createNavigatorFactory.js +27 -5
  28. package/lib/module/createNavigatorFactory.js.map +1 -1
  29. package/lib/module/findFocusedRoute.js +7 -4
  30. package/lib/module/findFocusedRoute.js.map +1 -1
  31. package/lib/module/getActionFromState.js +45 -20
  32. package/lib/module/getActionFromState.js.map +1 -1
  33. package/lib/module/getPathFromState.js +171 -90
  34. package/lib/module/getPathFromState.js.map +1 -1
  35. package/lib/module/getPatternParts.js +20 -4
  36. package/lib/module/getPatternParts.js.map +1 -1
  37. package/lib/module/getStateFromPath.js +485 -214
  38. package/lib/module/getStateFromPath.js.map +1 -1
  39. package/lib/module/getStateFromRouteParams.js +24 -0
  40. package/lib/module/getStateFromRouteParams.js.map +1 -0
  41. package/lib/module/index.js +5 -4
  42. package/lib/module/index.js.map +1 -1
  43. package/lib/module/theming/useTheme.js +1 -1
  44. package/lib/module/theming/useTheme.js.map +1 -1
  45. package/lib/module/types.js +35 -0
  46. package/lib/module/types.js.map +1 -1
  47. package/lib/module/useDescriptors.js +19 -11
  48. package/lib/module/useDescriptors.js.map +1 -1
  49. package/lib/module/useEventEmitter.js +48 -33
  50. package/lib/module/useEventEmitter.js.map +1 -1
  51. package/lib/module/useFocusEvents.js +12 -4
  52. package/lib/module/useFocusEvents.js.map +1 -1
  53. package/lib/module/useFocusedListenersChildrenAdapter.js +2 -2
  54. package/lib/module/useFocusedListenersChildrenAdapter.js.map +1 -1
  55. package/lib/module/useIsFocused.js +7 -12
  56. package/lib/module/useIsFocused.js.map +1 -1
  57. package/lib/module/useKeyedChildListeners.js +4 -2
  58. package/lib/module/useKeyedChildListeners.js.map +1 -1
  59. package/lib/module/useLazyValue.js.map +1 -1
  60. package/lib/module/useNavigation.js +2 -15
  61. package/lib/module/useNavigation.js.map +1 -1
  62. package/lib/module/useNavigationBuilder.js +201 -131
  63. package/lib/module/useNavigationBuilder.js.map +1 -1
  64. package/lib/module/useNavigationCache.js +22 -17
  65. package/lib/module/useNavigationCache.js.map +1 -1
  66. package/lib/module/useNavigationHelpers.js +28 -10
  67. package/lib/module/useNavigationHelpers.js.map +1 -1
  68. package/lib/module/useNavigationIndependentTree.js +1 -1
  69. package/lib/module/useNavigationIndependentTree.js.map +1 -1
  70. package/lib/module/useNavigationState.js +70 -35
  71. package/lib/module/useNavigationState.js.map +1 -1
  72. package/lib/module/useOnAction.js +37 -7
  73. package/lib/module/useOnAction.js.map +1 -1
  74. package/lib/module/useOnGetState.js +6 -3
  75. package/lib/module/useOnGetState.js.map +1 -1
  76. package/lib/module/useOnPreventRemove.js +39 -30
  77. package/lib/module/useOnPreventRemove.js.map +1 -1
  78. package/lib/module/useOnRouteFocus.js +1 -1
  79. package/lib/module/useOnRouteFocus.js.map +1 -1
  80. package/lib/module/useOptionsGetters.js +10 -5
  81. package/lib/module/useOptionsGetters.js.map +1 -1
  82. package/lib/module/usePreventRemove.js +10 -3
  83. package/lib/module/usePreventRemove.js.map +1 -1
  84. package/lib/module/usePreventRemoveContext.js +1 -1
  85. package/lib/module/usePreventRemoveContext.js.map +1 -1
  86. package/lib/module/useRegisterNavigator.js +2 -2
  87. package/lib/module/useRegisterNavigator.js.map +1 -1
  88. package/lib/module/useRoute.js +2 -0
  89. package/lib/module/useRoute.js.map +1 -1
  90. package/lib/module/useRouteCache.js +17 -11
  91. package/lib/module/useRouteCache.js.map +1 -1
  92. package/lib/module/useScheduleUpdate.js +9 -9
  93. package/lib/module/useScheduleUpdate.js.map +1 -1
  94. package/lib/module/useStateForPath.js +1 -1
  95. package/lib/module/useStateForPath.js.map +1 -1
  96. package/lib/module/useSyncState.js +7 -3
  97. package/lib/module/useSyncState.js.map +1 -1
  98. package/lib/module/validatePathConfig.js +50 -36
  99. package/lib/module/validatePathConfig.js.map +1 -1
  100. package/lib/typescript/src/BaseNavigationContainer.d.ts +6 -3
  101. package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
  102. package/lib/typescript/src/ConsumedParamsContext.d.ts +3 -0
  103. package/lib/typescript/src/ConsumedParamsContext.d.ts.map +1 -0
  104. package/lib/typescript/src/DataLoading.d.ts +21 -0
  105. package/lib/typescript/src/DataLoading.d.ts.map +1 -0
  106. package/lib/typescript/src/EnsureSingleNavigator.d.ts +1 -1
  107. package/lib/typescript/src/EnsureSingleNavigator.d.ts.map +1 -1
  108. package/lib/typescript/src/Group.d.ts +1 -1
  109. package/lib/typescript/src/Group.d.ts.map +1 -1
  110. package/lib/typescript/src/NavigationBuilderContext.d.ts +9 -8
  111. package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
  112. package/lib/typescript/src/NavigationContainerRefContext.d.ts +1 -1
  113. package/lib/typescript/src/NavigationContainerRefContext.d.ts.map +1 -1
  114. package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts +4 -4
  115. package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts.map +1 -1
  116. package/lib/typescript/src/NavigationHelpersContext.d.ts +1 -1
  117. package/lib/typescript/src/NavigationHelpersContext.d.ts.map +1 -1
  118. package/lib/typescript/src/NavigationIndependentTree.d.ts +1 -1
  119. package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
  120. package/lib/typescript/src/NavigationProvider.d.ts +12 -13
  121. package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
  122. package/lib/typescript/src/NavigationStateContext.d.ts +3 -3
  123. package/lib/typescript/src/NavigationStateContext.d.ts.map +1 -1
  124. package/lib/typescript/src/PreventRemoveContext.d.ts +1 -0
  125. package/lib/typescript/src/PreventRemoveContext.d.ts.map +1 -1
  126. package/lib/typescript/src/PreventRemoveProvider.d.ts +1 -1
  127. package/lib/typescript/src/PreventRemoveProvider.d.ts.map +1 -1
  128. package/lib/typescript/src/SceneView.d.ts +3 -2
  129. package/lib/typescript/src/SceneView.d.ts.map +1 -1
  130. package/lib/typescript/src/Screen.d.ts +1 -1
  131. package/lib/typescript/src/Screen.d.ts.map +1 -1
  132. package/lib/typescript/src/StaticContainer.d.ts +1 -1
  133. package/lib/typescript/src/StaticContainer.d.ts.map +1 -1
  134. package/lib/typescript/src/StaticNavigation.d.ts +189 -109
  135. package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
  136. package/lib/typescript/src/StaticTreeContext.d.ts +4 -0
  137. package/lib/typescript/src/StaticTreeContext.d.ts.map +1 -0
  138. package/lib/typescript/src/checkSerializable.d.ts +8 -4
  139. package/lib/typescript/src/checkSerializable.d.ts.map +1 -1
  140. package/lib/typescript/src/createNavigationContainerRef.d.ts +1 -1
  141. package/lib/typescript/src/createNavigationContainerRef.d.ts.map +1 -1
  142. package/lib/typescript/src/createNavigatorFactory.d.ts +23 -5
  143. package/lib/typescript/src/createNavigatorFactory.d.ts.map +1 -1
  144. package/lib/typescript/src/findFocusedRoute.d.ts +3 -3
  145. package/lib/typescript/src/findFocusedRoute.d.ts.map +1 -1
  146. package/lib/typescript/src/getActionFromState.d.ts +4 -4
  147. package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
  148. package/lib/typescript/src/getPathFromState.d.ts +3 -3
  149. package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
  150. package/lib/typescript/src/getPatternParts.d.ts +1 -0
  151. package/lib/typescript/src/getPatternParts.d.ts.map +1 -1
  152. package/lib/typescript/src/getStateFromPath.d.ts +5 -5
  153. package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
  154. package/lib/typescript/src/getStateFromRouteParams.d.ts +3 -0
  155. package/lib/typescript/src/getStateFromRouteParams.d.ts.map +1 -0
  156. package/lib/typescript/src/index.d.ts +36 -33
  157. package/lib/typescript/src/index.d.ts.map +1 -1
  158. package/lib/typescript/src/theming/ThemeContext.d.ts +1 -1
  159. package/lib/typescript/src/theming/ThemeContext.d.ts.map +1 -1
  160. package/lib/typescript/src/theming/ThemeProvider.d.ts +2 -2
  161. package/lib/typescript/src/theming/ThemeProvider.d.ts.map +1 -1
  162. package/lib/typescript/src/theming/useTheme.d.ts +1 -1
  163. package/lib/typescript/src/theming/useTheme.d.ts.map +1 -1
  164. package/lib/typescript/src/types.d.ts +260 -159
  165. package/lib/typescript/src/types.d.ts.map +1 -1
  166. package/lib/typescript/src/useChildListeners.d.ts +3 -3
  167. package/lib/typescript/src/useChildListeners.d.ts.map +1 -1
  168. package/lib/typescript/src/useComponent.d.ts +1 -1
  169. package/lib/typescript/src/useComponent.d.ts.map +1 -1
  170. package/lib/typescript/src/useDescriptors.d.ts +8 -8
  171. package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
  172. package/lib/typescript/src/useEventEmitter.d.ts +2 -2
  173. package/lib/typescript/src/useEventEmitter.d.ts.map +1 -1
  174. package/lib/typescript/src/useFocusEvents.d.ts +2 -2
  175. package/lib/typescript/src/useFocusEvents.d.ts.map +1 -1
  176. package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts +2 -2
  177. package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts.map +1 -1
  178. package/lib/typescript/src/useIsFocused.d.ts +3 -0
  179. package/lib/typescript/src/useIsFocused.d.ts.map +1 -1
  180. package/lib/typescript/src/useKeyedChildListeners.d.ts +3 -3
  181. package/lib/typescript/src/useKeyedChildListeners.d.ts.map +1 -1
  182. package/lib/typescript/src/useLazyValue.d.ts.map +1 -1
  183. package/lib/typescript/src/useNavigation.d.ts +4 -2
  184. package/lib/typescript/src/useNavigation.d.ts.map +1 -1
  185. package/lib/typescript/src/useNavigationBuilder.d.ts +23 -30
  186. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  187. package/lib/typescript/src/useNavigationCache.d.ts +3 -3
  188. package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
  189. package/lib/typescript/src/useNavigationContainerRef.d.ts +1 -1
  190. package/lib/typescript/src/useNavigationContainerRef.d.ts.map +1 -1
  191. package/lib/typescript/src/useNavigationHelpers.d.ts +19 -27
  192. package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
  193. package/lib/typescript/src/useNavigationState.d.ts +13 -3
  194. package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
  195. package/lib/typescript/src/useOnAction.d.ts +9 -9
  196. package/lib/typescript/src/useOnAction.d.ts.map +1 -1
  197. package/lib/typescript/src/useOnGetState.d.ts +1 -1
  198. package/lib/typescript/src/useOnGetState.d.ts.map +1 -1
  199. package/lib/typescript/src/useOnPreventRemove.d.ts +5 -9
  200. package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
  201. package/lib/typescript/src/useOnRouteFocus.d.ts +6 -6
  202. package/lib/typescript/src/useOnRouteFocus.d.ts.map +1 -1
  203. package/lib/typescript/src/useOptionsGetters.d.ts +1 -1
  204. package/lib/typescript/src/useOptionsGetters.d.ts.map +1 -1
  205. package/lib/typescript/src/usePreventRemove.d.ts.map +1 -1
  206. package/lib/typescript/src/usePreventRemoveContext.d.ts +2 -1
  207. package/lib/typescript/src/usePreventRemoveContext.d.ts.map +1 -1
  208. package/lib/typescript/src/useRoute.d.ts +7 -3
  209. package/lib/typescript/src/useRoute.d.ts.map +1 -1
  210. package/lib/typescript/src/useRouteCache.d.ts +5 -5
  211. package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
  212. package/lib/typescript/src/useScheduleUpdate.d.ts +5 -4
  213. package/lib/typescript/src/useScheduleUpdate.d.ts.map +1 -1
  214. package/lib/typescript/src/useStateForPath.d.ts +1 -1
  215. package/lib/typescript/src/useStateForPath.d.ts.map +1 -1
  216. package/lib/typescript/src/useSyncState.d.ts.map +1 -1
  217. package/lib/typescript/src/utilities.d.ts +74 -37
  218. package/lib/typescript/src/utilities.d.ts.map +1 -1
  219. package/lib/typescript/src/validatePathConfig.d.ts.map +1 -1
  220. package/package.json +23 -22
  221. package/src/BaseNavigationContainer.tsx +351 -333
  222. package/src/ConsumedParamsContext.tsx +5 -0
  223. package/src/DataLoading.tsx +203 -0
  224. package/src/NavigationBuilderContext.tsx +14 -9
  225. package/src/NavigationFocusedRouteStateContext.tsx +4 -4
  226. package/src/NavigationIndependentTree.tsx +18 -12
  227. package/src/NavigationProvider.tsx +26 -15
  228. package/src/NavigationStateContext.tsx +5 -6
  229. package/src/PreventRemoveContext.tsx +1 -0
  230. package/src/PreventRemoveProvider.tsx +51 -46
  231. package/src/SceneView.tsx +7 -39
  232. package/src/StaticNavigation.tsx +659 -307
  233. package/src/StaticTreeContext.tsx +7 -0
  234. package/src/checkSerializable.tsx +35 -19
  235. package/src/createNavigationContainerRef.tsx +12 -9
  236. package/src/createNavigatorFactory.tsx +92 -8
  237. package/src/findFocusedRoute.tsx +13 -8
  238. package/src/getActionFromState.tsx +86 -51
  239. package/src/getPathFromState.tsx +254 -128
  240. package/src/getPatternParts.tsx +25 -4
  241. package/src/getStateFromPath.tsx +811 -309
  242. package/src/getStateFromRouteParams.tsx +60 -0
  243. package/src/index.tsx +55 -5
  244. package/src/theming/useTheme.tsx +1 -1
  245. package/src/types.tsx +798 -373
  246. package/src/useDescriptors.tsx +28 -14
  247. package/src/useEventEmitter.tsx +52 -38
  248. package/src/useFocusEvents.tsx +15 -7
  249. package/src/useFocusedListenersChildrenAdapter.tsx +2 -2
  250. package/src/useIsFocused.tsx +14 -21
  251. package/src/useKeyedChildListeners.tsx +4 -2
  252. package/src/useLazyValue.tsx +2 -2
  253. package/src/useNavigation.tsx +7 -24
  254. package/src/useNavigationBuilder.tsx +311 -186
  255. package/src/useNavigationCache.tsx +32 -25
  256. package/src/useNavigationHelpers.tsx +27 -10
  257. package/src/useNavigationIndependentTree.tsx +1 -1
  258. package/src/useNavigationState.tsx +93 -59
  259. package/src/useOnAction.tsx +61 -13
  260. package/src/useOnGetState.tsx +5 -3
  261. package/src/useOnPreventRemove.tsx +75 -42
  262. package/src/useOnRouteFocus.tsx +10 -12
  263. package/src/useOptionsGetters.tsx +11 -5
  264. package/src/usePreventRemove.tsx +12 -3
  265. package/src/usePreventRemoveContext.tsx +1 -1
  266. package/src/useRegisterNavigator.tsx +2 -2
  267. package/src/useRoute.tsx +20 -6
  268. package/src/useRouteCache.tsx +22 -16
  269. package/src/useScheduleUpdate.tsx +9 -10
  270. package/src/useStateForPath.tsx +1 -1
  271. package/src/useSyncState.tsx +8 -3
  272. package/src/utilities.tsx +144 -31
  273. package/src/validatePathConfig.tsx +86 -57
  274. package/lib/module/CurrentRenderContext.js +0 -10
  275. package/lib/module/CurrentRenderContext.js.map +0 -1
  276. package/lib/module/useCurrentRender.js +0 -19
  277. package/lib/module/useCurrentRender.js.map +0 -1
  278. package/lib/typescript/src/CurrentRenderContext.d.ts +0 -9
  279. package/lib/typescript/src/CurrentRenderContext.d.ts.map +0 -1
  280. package/lib/typescript/src/useCurrentRender.d.ts +0 -14
  281. package/lib/typescript/src/useCurrentRender.d.ts.map +0 -1
  282. package/src/CurrentRenderContext.tsx +0 -9
  283. package/src/useCurrentRender.tsx +0 -31
@@ -1,6 +1,9 @@
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
 
@@ -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,27 @@ 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 } = React.use(NavigationBuilderContext);
111
+ const route = React.use(NavigationRouteContext);
83
112
  const routeKey = route?.key;
84
113
 
85
- React.useEffect(() => {
114
+ React.useInsertionEffect(() => {
86
115
  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
- });
116
+ return addKeyedListener?.(
117
+ 'beforeRemove',
118
+ routeKey,
119
+ (action, nextState) => {
120
+ const state = getState();
121
+
122
+ return shouldPreventRemove(
123
+ emitter,
124
+ beforeRemoveListeners,
125
+ state.routes,
126
+ nextState?.routes ?? [],
127
+ action
128
+ );
129
+ }
130
+ );
98
131
  }
99
132
  }, [addKeyedListener, beforeRemoveListeners, emitter, getState, routeKey]);
100
133
  }
@@ -7,11 +7,11 @@ import * as React from 'react';
7
7
 
8
8
  import { NavigationBuilderContext } 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,13 +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(
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 } = React.use(
29
27
  NavigationBuilderContext
30
28
  );
31
29
 
@@ -17,8 +17,8 @@ export function useOptionsGetters({ key, options, navigation }: Options) {
17
17
  Record<string, () => object | undefined | null>
18
18
  >({});
19
19
 
20
- const { onOptionsChange } = React.useContext(NavigationBuilderContext);
21
- const { addOptionsGetter: parentAddOptionsGetter } = React.useContext(
20
+ const { onOptionsChange } = React.use(NavigationBuilderContext);
21
+ const { addOptionsGetter: parentAddOptionsGetter } = React.use(
22
22
  NavigationStateContext
23
23
  );
24
24
 
@@ -31,8 +31,11 @@ export function useOptionsGetters({ key, options, navigation }: Options) {
31
31
  }
32
32
  }, [navigation, onOptionsChange]);
33
33
 
34
- React.useEffect(() => {
34
+ React.useInsertionEffect(() => {
35
35
  optionsRef.current = options;
36
+ }, [options]);
37
+
38
+ React.useEffect(() => {
36
39
  optionsChangeListener();
37
40
 
38
41
  return navigation?.addListener('focus', optionsChangeListener);
@@ -69,8 +72,11 @@ export function useOptionsGetters({ key, options, navigation }: Options) {
69
72
  return optionsRef.current;
70
73
  }, [navigation, getOptionsFromListener]);
71
74
 
72
- React.useEffect(() => {
73
- return parentAddOptionsGetter?.(key!, getCurrentOptions);
75
+ React.useInsertionEffect(() => {
76
+ // We don't have a parent at the root
77
+ if (key != null) {
78
+ return parentAddOptionsGetter?.(key, getCurrentOptions);
79
+ }
74
80
  }, [getCurrentOptions, parentAddOptionsGetter, key]);
75
81
 
76
82
  const addOptionsGetter = React.useCallback(
@@ -23,15 +23,24 @@ export function usePreventRemove(
23
23
  const navigation = useNavigation();
24
24
  const { key: routeKey } = useRoute();
25
25
 
26
- const { setPreventRemove } = usePreventRemoveContext();
26
+ const { setPreventRemove, notifyPreventRemove } = usePreventRemoveContext();
27
27
 
28
- React.useEffect(() => {
28
+ React.useInsertionEffect(() => {
29
29
  setPreventRemove(id, routeKey, preventRemove);
30
+
30
31
  return () => {
31
32
  setPreventRemove(id, routeKey, false);
32
33
  };
33
34
  }, [setPreventRemove, id, routeKey, preventRemove]);
34
35
 
36
+ React.useEffect(() => {
37
+ notifyPreventRemove();
38
+
39
+ return () => {
40
+ notifyPreventRemove();
41
+ };
42
+ }, [id, routeKey, preventRemove, notifyPreventRemove]);
43
+
35
44
  const beforeRemoveListener = useLatestCallback<
36
45
  EventListenerCallback<EventMapCore<any>, 'beforeRemove'>
37
46
  >((e) => {
@@ -44,7 +53,7 @@ export function usePreventRemove(
44
53
  callback({ data: e.data });
45
54
  });
46
55
 
47
- React.useEffect(
56
+ React.useInsertionEffect(
48
57
  () => navigation?.addListener('beforeRemove', beforeRemoveListener),
49
58
  [navigation, beforeRemoveListener]
50
59
  );
@@ -3,7 +3,7 @@ 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(
@@ -9,7 +9,7 @@ import { SingleNavigatorContext } from './EnsureSingleNavigator';
9
9
  */
10
10
  export function useRegisterNavigator() {
11
11
  const [key] = React.useState(() => nanoid());
12
- const container = React.useContext(SingleNavigatorContext);
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.
@@ -21,9 +19,12 @@ export function useRouteCache<State extends NavigationState>(
21
19
  routes: State['routes']
22
20
  ) {
23
21
  // Cache object which holds route objects for each screen
24
- const cache = React.useMemo(() => ({ current: new Map() as RouteCache }), []);
22
+ const cache = React.useMemo(
23
+ () => ({ current: new Map<string, RouteProp<ParamListBase>>() }),
24
+ []
25
+ );
25
26
 
26
- cache.current = routes.reduce((acc, route) => {
27
+ const next = routes.reduce((acc, route) => {
27
28
  const previous = cache.current.get(route.key);
28
29
  const { state, ...routeWithoutState } = route;
29
30
 
@@ -36,13 +37,11 @@ export function useRouteCache<State extends NavigationState>(
36
37
  proxy = routeWithoutState;
37
38
  }
38
39
 
39
- if (process.env.NODE_ENV !== 'production') {
40
+ if (process.env.NODE_ENV !== 'production' && proxy !== previous) {
40
41
  // FIXME: since the state is updated with mutation, the route object cannot be frozen
41
42
  // 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];
45
-
43
+ // Only needed once per proxy - skip if we're reusing a previously-frozen one
44
+ for (const [key, value] of Object.entries(proxy)) {
46
45
  Object.defineProperty(proxy, key, {
47
46
  enumerable: true,
48
47
  configurable: true,
@@ -52,16 +51,23 @@ export function useRouteCache<State extends NavigationState>(
52
51
  }
53
52
  }
54
53
 
55
- Object.defineProperty(proxy, CHILD_STATE, {
56
- enumerable: false,
57
- configurable: true,
58
- value: state,
59
- });
54
+ // @ts-expect-error: this isn't in type definitions coz we want this private
55
+ if (proxy[CHILD_STATE] !== state) {
56
+ Object.defineProperty(proxy, CHILD_STATE, {
57
+ enumerable: false,
58
+ configurable: true,
59
+ value: state,
60
+ });
61
+ }
60
62
 
61
63
  acc.set(route.key, proxy);
62
64
 
63
65
  return acc;
64
- }, new Map() as RouteCache);
66
+ }, new Map<string, RouteProp<ParamListBase>>());
67
+
68
+ React.useInsertionEffect(() => {
69
+ cache.current = next;
70
+ });
65
71
 
66
- return Array.from(cache.current.values());
72
+ return Array.from(next.values());
67
73
  }
@@ -4,19 +4,18 @@ import { NavigationBuilderContext } from './NavigationBuilderContext';
4
4
 
5
5
  /**
6
6
  * 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.
7
+ * However, navigation state is in the root component and React won't let us update it from a child during render.
8
+ * The render could also be interrupted in concurrent mode, so we need to update after commit.
9
+ * So we queue the update in an insertion effect and flush it in a layout effect.
10
+ * Insertion effects run before layout effects, so every navigator in the tree has queued its update before we flush.
11
+ * This lets nested updates be applied from the root down without clobbering each other.
11
12
  */
12
13
  export function useScheduleUpdate(callback: () => void) {
13
- const { scheduleUpdate, flushUpdates } = React.useContext(
14
- NavigationBuilderContext
15
- );
14
+ const { scheduleUpdate, flushUpdates } = React.use(NavigationBuilderContext);
16
15
 
17
- // FIXME: This is potentially unsafe
18
- // However, since we are using sync store, it might be fine
19
- scheduleUpdate(callback);
16
+ React.useInsertionEffect(() => {
17
+ scheduleUpdate(callback);
18
+ });
20
19
 
21
20
  React.useLayoutEffect(flushUpdates);
22
21
  }
@@ -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
  }
@@ -26,9 +26,10 @@ const createStore = <T,>(getInitialState: () => T) => {
26
26
 
27
27
  const setState = (newState: T) => {
28
28
  state = deepFreeze(newState);
29
- didUpdate = true;
30
29
 
31
- if (!isBatching) {
30
+ if (isBatching) {
31
+ didUpdate = true;
32
+ } else {
32
33
  listeners.forEach((listener) => listener());
33
34
  }
34
35
  };
@@ -100,7 +101,11 @@ export function useSyncState<T>(getInitialState: () => T) {
100
101
 
101
102
  if (pendingUpdates.length !== 0) {
102
103
  store.batchUpdates(() => {
103
- // Flush all the pending updates
104
+ // Updates are queued child-first as insertion effects run bottom-up.
105
+ // The root navigator replaces the whole state while nested navigators merge into it.
106
+ // So we apply them root-first to avoid the root clobbering nested updates.
107
+ pendingUpdates.reverse();
108
+
104
109
  for (const update of pendingUpdates) {
105
110
  update();
106
111
  }