@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
@@ -23,8 +23,8 @@ type Options<
23
23
  Partial<NavigationProp<ParamListBase, string, any, any, any>>;
24
24
  setOptions: (
25
25
  cb: (
26
- options: Record<string, ScreenOptions>
27
- ) => Record<string, ScreenOptions>
26
+ options: Record<string, Partial<ScreenOptions> | undefined>
27
+ ) => Record<string, Partial<ScreenOptions> | undefined>
28
28
  ) => void;
29
29
  router: Router<State, NavigationAction>;
30
30
  emitter: NavigationEventEmitter<EventMap>;
@@ -72,8 +72,8 @@ export function useNavigationCache<
72
72
  router,
73
73
  emitter,
74
74
  }: Options<State, ScreenOptions, EventMap>) {
75
- const parentNavigationHelpers = React.useContext(NavigationContext);
76
- const { stackRef } = React.useContext(NavigationBuilderContext);
75
+ const parentNavigationHelpers = React.use(NavigationContext);
76
+ const { stackRef } = React.use(NavigationBuilderContext);
77
77
 
78
78
  // Cache object which holds navigation objects for each screen
79
79
  // We use `React.useMemo` instead of `React.useRef` coz we want to invalidate it when deps change
@@ -91,27 +91,15 @@ export function useNavigationCache<
91
91
  [getState, parentNavigationHelpers, navigation, setOptions, emitter]
92
92
  );
93
93
 
94
- cache.current = routes.reduce<
94
+ const next = routes.reduce<
95
95
  NavigationCache<State, ScreenOptions, EventMap, ActionHelpers>
96
96
  >((acc, route) => {
97
97
  const previous = cache.current[route.key];
98
98
 
99
- type Thunk =
100
- | NavigationAction
101
- | ((state: State) => NavigationAction | null | undefined);
102
-
103
99
  if (previous) {
104
100
  // If a cached navigation object already exists, reuse it
105
101
  acc[route.key] = previous;
106
102
  } else {
107
- const dispatch = (thunk: Thunk) => {
108
- const action = typeof thunk === 'function' ? thunk(getState()) : thunk;
109
-
110
- if (action != null) {
111
- navigation.dispatch({ source: route.key, ...action });
112
- }
113
- };
114
-
115
103
  const withStack = (callback: () => void) => {
116
104
  let isStackSet = false;
117
105
 
@@ -134,6 +122,23 @@ export function useNavigationCache<
134
122
  }
135
123
  };
136
124
 
125
+ const dispatch = (
126
+ thunk:
127
+ | NavigationAction
128
+ | ((state: State) => NavigationAction | null | undefined)
129
+ ) => {
130
+ withStack(() =>
131
+ React.startTransition(() => {
132
+ const action =
133
+ typeof thunk === 'function' ? thunk(getState()) : thunk;
134
+
135
+ if (action != null) {
136
+ navigation.dispatch({ source: route.key, ...action });
137
+ }
138
+ })
139
+ );
140
+ };
141
+
137
142
  const actions = {
138
143
  ...router.actionCreators,
139
144
  ...CommonActions,
@@ -142,10 +147,8 @@ export function useNavigationCache<
142
147
  const helpers = Object.keys(actions).reduce<Record<string, () => void>>(
143
148
  (acc, name) => {
144
149
  acc[name] = (...args: any) =>
145
- withStack(() =>
146
- // @ts-expect-error: name is a valid key, but TypeScript is dumb
147
- dispatch(actions[name](...args))
148
- );
150
+ // @ts-expect-error: name is a valid key, but TypeScript is dumb
151
+ dispatch(actions[name](...args));
149
152
 
150
153
  return acc;
151
154
  },
@@ -160,7 +163,7 @@ export function useNavigationCache<
160
163
  ...helpers,
161
164
  // FIXME: too much work to fix the types for now
162
165
  ...(emitter.create(route.key) as any),
163
- dispatch: (thunk: Thunk) => withStack(() => dispatch(thunk)),
166
+ dispatch,
164
167
  getParent: (routeName) => {
165
168
  if (routeName === route.name) {
166
169
  // If the passed route name is the same as the current route's name,
@@ -174,7 +177,7 @@ export function useNavigationCache<
174
177
 
175
178
  return parentNavigationHelpers;
176
179
  },
177
- setOptions: (options: object) => {
180
+ setOptions: (options: Partial<ScreenOptions>) => {
178
181
  setOptions((o) => ({
179
182
  ...o,
180
183
  [route.key]: { ...o[route.key], ...options },
@@ -183,7 +186,7 @@ export function useNavigationCache<
183
186
  isFocused: () => {
184
187
  const state = rest.getState();
185
188
 
186
- if (state.routes[state.index].key !== route.key) {
189
+ if (state.routes[state.index]?.key !== route.key) {
187
190
  return false;
188
191
  }
189
192
 
@@ -197,5 +200,9 @@ export function useNavigationCache<
197
200
  return acc;
198
201
  }, {});
199
202
 
200
- return cache.current;
203
+ React.useInsertionEffect(() => {
204
+ cache.current = next;
205
+ });
206
+
207
+ return next;
201
208
  }
@@ -19,9 +19,9 @@ type Options<State extends NavigationState, Action extends NavigationAction> = {
19
19
  onAction: (action: NavigationAction) => boolean;
20
20
  onUnhandledAction: (action: NavigationAction) => void;
21
21
  getState: () => State;
22
+ state: State;
22
23
  emitter: NavigationEventEmitter<any>;
23
24
  router: Router<State, Action>;
24
- stateRef: React.RefObject<State | null>;
25
25
  };
26
26
 
27
27
  /**
@@ -37,11 +37,28 @@ export function useNavigationHelpers<
37
37
  onAction,
38
38
  onUnhandledAction,
39
39
  getState,
40
+ state,
40
41
  emitter,
41
42
  router,
42
- stateRef,
43
43
  }: Options<State, Action>) {
44
- const parentNavigationHelpers = React.useContext(NavigationContext);
44
+ const parentNavigationHelpers = React.use(NavigationContext);
45
+
46
+ // In some cases (e.g. route names change), internal state might have changed
47
+ // But it hasn't been committed yet, so hasn't propagated to any listeners
48
+ // During this time, we need to return the internal state in `getState`
49
+ // Otherwise it can return an inconsistent state during render in children
50
+ // This may affect hooks like `useNavigationState` that need to read the state during render
51
+ // To avoid this, we use a ref for render phase, and immediately clear it on commit
52
+ // The ref won't be cleared if the render is discarded, e.g. when interrupted
53
+ // So we also track the stored state the render was based on
54
+ // Then ignore the ref once the stored state changes due to another dispatch
55
+ const stateRef = React.useRef<{ state: State; base: State } | null>(null);
56
+
57
+ stateRef.current = { state, base: getState() };
58
+
59
+ React.useInsertionEffect(() => {
60
+ stateRef.current = null;
61
+ });
45
62
 
46
63
  return React.useMemo(() => {
47
64
  const dispatch = (op: Action | ((state: State) => Action)) => {
@@ -87,16 +104,16 @@ export function useNavigationHelpers<
87
104
  );
88
105
  },
89
106
  getState: (): State => {
107
+ const current = getState();
108
+ const pending = stateRef.current;
109
+
90
110
  // FIXME: Workaround for when the state is read during render
91
- // By this time, we haven't committed the new state yet
92
- // Without this `useSyncExternalStore` will keep reading the old state
93
- // This may result in `useNavigationState` or `useIsFocused` returning wrong values
94
- // Apart from `useSyncExternalStore`, `getState` should never be called during render
95
- if (stateRef.current != null) {
96
- return stateRef.current;
111
+ // Apart from subscriptions, `getState` should never be called during render
112
+ if (pending != null && pending.base === current) {
113
+ return pending.state;
97
114
  }
98
115
 
99
- return getState();
116
+ return current;
100
117
  },
101
118
  } as NavigationHelpers<ParamListBase, EventMap> & ActionHelpers;
102
119
 
@@ -3,5 +3,5 @@ import * as React from 'react';
3
3
  import { NavigationIndependentTreeContext } from './NavigationIndependentTreeContext';
4
4
 
5
5
  export function useNavigationIndependentTree() {
6
- return React.useContext(NavigationIndependentTreeContext);
6
+ return React.use(NavigationIndependentTreeContext);
7
7
  }
@@ -1,14 +1,8 @@
1
1
  import type { NavigationState, ParamListBase } from '@react-navigation/routers';
2
2
  import * as React from 'react';
3
3
  import useLatestCallback from 'use-latest-callback';
4
- import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector';
5
4
 
6
- import type {
7
- NavigationListForNested,
8
- NavigationProp,
9
- RootNavigator,
10
- } from './types';
11
- import { useNavigation } from './useNavigation';
5
+ import type { NavigationListForNested, RootNavigator } from './types';
12
6
 
13
7
  type NavigationStateListener = {
14
8
  getState: () => NavigationState<ParamListBase>;
@@ -35,8 +29,8 @@ type NavigationStateForNested<
35
29
  export function useNavigationState<
36
30
  const T,
37
31
  const Navigator = RootNavigator,
38
- const RouteName extends
39
- keyof NavigationListForNested<Navigator> = keyof NavigationListForNested<Navigator>,
32
+ const RouteName extends keyof NavigationListForNested<Navigator> =
33
+ keyof NavigationListForNested<Navigator>,
40
34
  >(
41
35
  routeName: RouteName,
42
36
  selector: (state: NavigationStateForNested<Navigator, RouteName>) => T
@@ -45,81 +39,84 @@ export function useNavigationState<T>(
45
39
  selector: (state: NavigationState<ParamListBase>) => T
46
40
  ): T;
47
41
  export function useNavigationState(...args: unknown[]): unknown {
48
- let navigation: NavigationProp<ParamListBase> | undefined,
49
- stateListener: NavigationStateListener | undefined,
50
- selector;
42
+ const selector = typeof args[0] === 'string' ? args[1] : args[0];
51
43
 
52
- if (typeof args[0] === 'string') {
53
- // `useNavigation` uses `use` internally, so it's fine to call it conditionally
54
- // @ts-expect-error we can't specify the type here
55
- // eslint-disable-next-line react-hooks/rules-of-hooks
56
- navigation = useNavigation(args[0]);
57
- selector = args[1];
58
- } else {
59
- selector = args[0];
44
+ if (typeof selector !== 'function') {
45
+ throw new Error(
46
+ `A selector function must be provided (got ${typeof selector}).`
47
+ );
60
48
  }
61
49
 
62
- if (navigation == null) {
63
- stateListener = React.use(NavigationStateListenerContext);
50
+ // @ts-expect-error: the public overloads guarantee the selector's type, but
51
+ // the `unknown[]` implementation signature can't express it
52
+ const select: (state: NavigationState<ParamListBase>) => unknown = selector;
53
+
54
+ let listener: NavigationStateListener | undefined;
55
+
56
+ if (typeof args[0] === 'string') {
57
+ const name = args[0];
58
+ const listeners = React.use(NamedNavigationStateListenerListContext);
59
+
60
+ listener = listeners?.[name];
61
+
62
+ if (listener == null) {
63
+ throw new Error(
64
+ `Couldn't find a navigator for the route '${name}' in any of the parent screens. Is your component inside the correct screen?`
65
+ );
66
+ }
67
+ } else {
68
+ listener = React.use(NavigationStateListenerContext);
64
69
 
65
- if (stateListener == null) {
70
+ if (listener == null) {
66
71
  throw new Error(
67
72
  "Couldn't get the navigation state. Is your component inside a navigator?"
68
73
  );
69
74
  }
70
75
  }
71
76
 
72
- const subscribe = React.useCallback(
73
- (callback: () => void) => {
74
- if (navigation) {
75
- return navigation.addListener('state', callback);
76
- } else if (stateListener) {
77
- return stateListener.subscribe(callback);
78
- } else {
79
- throw new Error(
80
- "Couldn't subscribe to navigation state changes. This is not expected."
81
- );
82
- }
83
- },
84
- [navigation, stateListener]
85
- );
77
+ const { getState, subscribe } = listener;
86
78
 
87
- const getSnapshot = navigation
88
- ? navigation.getState
89
- : stateListener?.getState;
79
+ const [, forceUpdate] = React.useReducer((count: number) => count + 1, 0);
90
80
 
91
- if (getSnapshot == null) {
92
- throw new Error("Couldn't get the navigation state. This is not expected.");
93
- }
81
+ const selected = select(getState());
94
82
 
95
- if (typeof selector !== 'function') {
96
- throw new Error(
97
- `A selector function must be provided (got ${typeof selector}).`
98
- );
99
- }
83
+ const selectionRef = React.useRef({ select, selected });
100
84
 
101
- const value = useSyncExternalStoreWithSelector(
102
- subscribe,
103
- getSnapshot,
104
- getSnapshot,
105
- // @ts-expect-error we can't infer the type here
106
- selector
107
- );
85
+ React.useLayoutEffect(() => {
86
+ selectionRef.current = { select, selected };
87
+ });
108
88
 
109
- return value;
89
+ React.useEffect(() => {
90
+ const checkForUpdates = () => {
91
+ const selection = selectionRef.current;
92
+
93
+ if (!Object.is(selection.selected, selection.select(getState()))) {
94
+ forceUpdate();
95
+ }
96
+ };
97
+
98
+ const unsubscribe = subscribe(checkForUpdates);
99
+
100
+ // The state may have changed between the render and the subscription
101
+ checkForUpdates();
102
+
103
+ return unsubscribe;
104
+ }, [getState, subscribe]);
105
+
106
+ return selected;
110
107
  }
111
108
 
112
109
  export function NavigationStateListenerProvider({
113
110
  state,
111
+ getState,
114
112
  children,
115
113
  }: {
116
114
  state: NavigationState<ParamListBase>;
115
+ getState: () => NavigationState<ParamListBase>;
117
116
  children: React.ReactNode;
118
117
  }) {
119
118
  const listeners = React.useRef<(() => void)[]>([]);
120
119
 
121
- const getState = useLatestCallback(() => state);
122
-
123
120
  const subscribe = useLatestCallback((callback: () => void) => {
124
121
  listeners.current.push(callback);
125
122
 
@@ -128,7 +125,8 @@ export function NavigationStateListenerProvider({
128
125
  };
129
126
  });
130
127
 
131
- React.useEffect(() => {
128
+ // Notify subscribers once the new state has committed
129
+ React.useLayoutEffect(() => {
132
130
  listeners.current.forEach((callback) => callback());
133
131
  }, [state]);
134
132
 
@@ -147,6 +145,42 @@ export function NavigationStateListenerProvider({
147
145
  );
148
146
  }
149
147
 
148
+ export function NamedNavigationStateListenerProvider({
149
+ name,
150
+ children,
151
+ }: {
152
+ name: string;
153
+ children: React.ReactNode;
154
+ }) {
155
+ const listener = React.use(NavigationStateListenerContext);
156
+
157
+ if (listener == null) {
158
+ throw new Error(
159
+ "Couldn't find a navigation state listener. This is likely because the navigator doesn't render its content under 'NavigationContent'."
160
+ );
161
+ }
162
+
163
+ const parents = React.use(NamedNavigationStateListenerListContext);
164
+
165
+ const listeners = React.useMemo(
166
+ () => ({
167
+ ...parents,
168
+ [name]: listener,
169
+ }),
170
+ [listener, name, parents]
171
+ );
172
+
173
+ return (
174
+ <NamedNavigationStateListenerListContext.Provider value={listeners}>
175
+ {children}
176
+ </NamedNavigationStateListenerListContext.Provider>
177
+ );
178
+ }
179
+
150
180
  const NavigationStateListenerContext = React.createContext<
151
181
  NavigationStateListener | undefined
152
182
  >(undefined);
183
+
184
+ export const NamedNavigationStateListenerListContext = React.createContext<
185
+ Record<string, NavigationStateListener> | undefined
186
+ >(undefined);
@@ -7,20 +7,23 @@ import type {
7
7
  } from '@react-navigation/routers';
8
8
  import * as React from 'react';
9
9
 
10
+ import { ConsumedParamsContext } from './ConsumedParamsContext';
11
+ import { getLoaderForStateChange } from './DataLoading';
10
12
  import {
11
13
  type ChildActionListener,
12
14
  type ChildBeforeRemoveListener,
13
15
  NavigationBuilderContext,
14
16
  } from './NavigationBuilderContext';
17
+ import { StaticTreeContext } from './StaticTreeContext';
15
18
  import type { EventMapCore } from './types';
16
19
  import type { NavigationEventEmitter } from './useEventEmitter';
17
20
  import { shouldPreventRemove, useOnPreventRemove } from './useOnPreventRemove';
18
21
 
19
- type Options = {
20
- router: Router<NavigationState, NavigationAction>;
21
- key?: string;
22
- getState: () => NavigationState;
23
- setState: (state: NavigationState | PartialState<NavigationState>) => void;
22
+ type Options<State extends NavigationState> = {
23
+ router: Router<State, NavigationAction>;
24
+ key?: string | undefined;
25
+ getState: () => State;
26
+ setState: (state: State | PartialState<State>) => void;
24
27
  actionListeners: ChildActionListener[];
25
28
  beforeRemoveListeners: Record<string, ChildBeforeRemoveListener | undefined>;
26
29
  routerConfigOptions: RouterConfigOptions;
@@ -36,7 +39,7 @@ type Options = {
36
39
  *
37
40
  * When the action handler handles as action, it returns `true`, otherwise `false`.
38
41
  */
39
- export function useOnAction({
42
+ export function useOnAction<State extends NavigationState>({
40
43
  router,
41
44
  getState,
42
45
  setState,
@@ -45,18 +48,22 @@ export function useOnAction({
45
48
  beforeRemoveListeners,
46
49
  routerConfigOptions,
47
50
  emitter,
48
- }: Options) {
51
+ }: Options<State>) {
49
52
  const {
50
53
  onAction: onActionParent,
51
54
  onRouteFocus: onRouteFocusParent,
52
55
  addListener: addListenerParent,
53
56
  onDispatchAction,
54
- } = React.useContext(NavigationBuilderContext);
57
+ flushUpdates,
58
+ } = React.use(NavigationBuilderContext);
59
+
60
+ const tree = React.use(StaticTreeContext);
61
+ const consumedParams = React.use(ConsumedParamsContext);
55
62
 
56
63
  const routerConfigOptionsRef =
57
64
  React.useRef<RouterConfigOptions>(routerConfigOptions);
58
65
 
59
- React.useEffect(() => {
66
+ React.useInsertionEffect(() => {
60
67
  routerConfigOptionsRef.current = routerConfigOptions;
61
68
  });
62
69
 
@@ -65,6 +72,11 @@ export function useOnAction({
65
72
  action: NavigationAction,
66
73
  visitedNavigators: Set<string> = new Set<string>()
67
74
  ) => {
75
+ // Apply any pending state updates scheduled during render before handling the action
76
+ // Otherwise the action will be handled with an outdated state,
77
+ // and the pending updates will overwrite the changes from the action
78
+ flushUpdates();
79
+
68
80
  const state = getState();
69
81
 
70
82
  // Since actions can bubble both up and down, they could come to the same navigator again
@@ -87,9 +99,18 @@ export function useOnAction({
87
99
  result =
88
100
  result === null && action.target === state.key ? state : result;
89
101
 
90
- if (result !== null) {
91
- onDispatchAction(action, state === result);
102
+ if (result !== null && result.stale !== false) {
103
+ // Some actions (e.g. `RESET`) may return a stale state from the router
104
+ // We rehydrate it before storing so the stored state is always usable
105
+ // Otherwise anything reading the state before the next render
106
+ // (e.g. another dispatch immediately after) would get an outdated state
107
+ result = router.getRehydratedState(
108
+ result,
109
+ routerConfigOptionsRef.current
110
+ );
111
+ }
92
112
 
113
+ if (result !== null) {
93
114
  if (state !== result) {
94
115
  const isPrevented = shouldPreventRemove(
95
116
  emitter,
@@ -100,10 +121,34 @@ export function useOnAction({
100
121
  );
101
122
 
102
123
  if (isPrevented) {
124
+ onDispatchAction(action, true);
125
+
103
126
  return true;
104
127
  }
105
128
 
129
+ if (getState() !== state) {
130
+ // Listeners for 'beforeRemove' may dispatch actions that change the state
131
+ // Then the result we have is outdated and would overwrite those changes
132
+ // So we handle the action again to calculate the result with the latest state
133
+ return onAction(action, new Set<string>());
134
+ }
135
+
136
+ onDispatchAction(action, false);
137
+
138
+ if (tree) {
139
+ const loader = getLoaderForStateChange(
140
+ tree,
141
+ result,
142
+ state,
143
+ consumedParams
144
+ );
145
+
146
+ loader?.();
147
+ }
148
+
106
149
  setState(result);
150
+ } else {
151
+ onDispatchAction(action, true);
107
152
  }
108
153
 
109
154
  if (onRouteFocusParent !== undefined) {
@@ -132,7 +177,7 @@ export function useOnAction({
132
177
  for (let i = actionListeners.length - 1; i >= 0; i--) {
133
178
  const listener = actionListeners[i];
134
179
 
135
- if (listener(action, visitedNavigators)) {
180
+ if (listener?.(action, visitedNavigators)) {
136
181
  return true;
137
182
  }
138
183
  }
@@ -143,7 +188,9 @@ export function useOnAction({
143
188
  [
144
189
  actionListeners,
145
190
  beforeRemoveListeners,
191
+ consumedParams,
146
192
  emitter,
193
+ flushUpdates,
147
194
  getState,
148
195
  key,
149
196
  onActionParent,
@@ -151,6 +198,7 @@ export function useOnAction({
151
198
  onRouteFocusParent,
152
199
  router,
153
200
  setState,
201
+ tree,
154
202
  ]
155
203
  );
156
204
 
@@ -160,7 +208,7 @@ export function useOnAction({
160
208
  beforeRemoveListeners,
161
209
  });
162
210
 
163
- React.useEffect(
211
+ React.useInsertionEffect(
164
212
  () => addListenerParent?.('action', onAction),
165
213
  [addListenerParent, onAction]
166
214
  );
@@ -14,8 +14,8 @@ type Options = {
14
14
  };
15
15
 
16
16
  export function useOnGetState({ getState, getStateListeners }: Options) {
17
- const { addKeyedListener } = React.useContext(NavigationBuilderContext);
18
- const route = React.useContext(NavigationRouteContext);
17
+ const { addKeyedListener } = React.use(NavigationBuilderContext);
18
+ const route = React.use(NavigationRouteContext);
19
19
  const key = route ? route.key : 'root';
20
20
 
21
21
  const getRehydratedState = React.useCallback(() => {
@@ -39,7 +39,9 @@ export function useOnGetState({ getState, getStateListeners }: Options) {
39
39
  return { ...state, routes };
40
40
  }, [getState, getStateListeners]);
41
41
 
42
- React.useEffect(() => {
42
+ // Use insertion effect to preserve the listener under `<Activity mode="hidden">`
43
+ // So `getRootState` will still return state of nested navigators
44
+ React.useInsertionEffect(() => {
43
45
  return addKeyedListener?.('getState', key, getRehydratedState);
44
46
  }, [addKeyedListener, getRehydratedState, key]);
45
47
  }