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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/README.md +17 -14
  2. package/lib/module/BaseNavigationContainer.js +168 -87
  3. package/lib/module/BaseNavigationContainer.js.map +1 -1
  4. package/lib/module/ConsumedParamsContext.js +5 -0
  5. package/lib/module/ConsumedParamsContext.js.map +1 -0
  6. package/lib/module/DataLoading.js +129 -0
  7. package/lib/module/DataLoading.js.map +1 -0
  8. package/lib/module/NavigationBuilderContext.js +7 -10
  9. package/lib/module/NavigationBuilderContext.js.map +1 -1
  10. package/lib/module/NavigationIndependentTree.js +54 -9
  11. package/lib/module/NavigationIndependentTree.js.map +1 -1
  12. package/lib/module/NavigationProvider.js +36 -11
  13. package/lib/module/NavigationProvider.js.map +1 -1
  14. package/lib/module/NavigationRootContext.js +8 -0
  15. package/lib/module/NavigationRootContext.js.map +1 -0
  16. package/lib/module/NavigationStateContext.js +3 -0
  17. package/lib/module/NavigationStateContext.js.map +1 -1
  18. package/lib/module/PreventRemoveContext.js.map +1 -1
  19. package/lib/module/PreventRemoveProvider.js +40 -31
  20. package/lib/module/PreventRemoveProvider.js.map +1 -1
  21. package/lib/module/SceneView.js +12 -42
  22. package/lib/module/SceneView.js.map +1 -1
  23. package/lib/module/StaticNavigation.js +219 -67
  24. package/lib/module/StaticNavigation.js.map +1 -1
  25. package/lib/module/StaticTreeContext.js +5 -0
  26. package/lib/module/StaticTreeContext.js.map +1 -0
  27. package/lib/module/createNavigationContainerRef.js +7 -9
  28. package/lib/module/createNavigationContainerRef.js.map +1 -1
  29. package/lib/module/createNavigatorFactory.js +27 -5
  30. package/lib/module/createNavigatorFactory.js.map +1 -1
  31. package/lib/module/findFocusedRoute.js +7 -4
  32. package/lib/module/findFocusedRoute.js.map +1 -1
  33. package/lib/module/getActionFromState.js +45 -20
  34. package/lib/module/getActionFromState.js.map +1 -1
  35. package/lib/module/getPathFromState.js +204 -94
  36. package/lib/module/getPathFromState.js.map +1 -1
  37. package/lib/module/getPatternParts.js +61 -14
  38. package/lib/module/getPatternParts.js.map +1 -1
  39. package/lib/module/getStateFromPath.js +608 -272
  40. package/lib/module/getStateFromPath.js.map +1 -1
  41. package/lib/module/getStateFromRouteParams.js +23 -0
  42. package/lib/module/getStateFromRouteParams.js.map +1 -0
  43. package/lib/module/index.js +7 -6
  44. package/lib/module/index.js.map +1 -1
  45. package/lib/module/internal.js +2 -0
  46. package/lib/module/internal.js.map +1 -0
  47. package/lib/module/isRecordEqual.js +1 -1
  48. package/lib/module/isRecordEqual.js.map +1 -1
  49. package/lib/module/queryString.js +71 -0
  50. package/lib/module/queryString.js.map +1 -0
  51. package/lib/module/theming/useTheme.js +1 -1
  52. package/lib/module/theming/useTheme.js.map +1 -1
  53. package/lib/module/types.js +35 -1
  54. package/lib/module/types.js.map +1 -1
  55. package/lib/module/useDescriptors.js +65 -25
  56. package/lib/module/useDescriptors.js.map +1 -1
  57. package/lib/module/useEventEmitter.js +48 -33
  58. package/lib/module/useEventEmitter.js.map +1 -1
  59. package/lib/module/useFocusEffect.js +4 -2
  60. package/lib/module/useFocusEffect.js.map +1 -1
  61. package/lib/module/useFocusEvents.js +15 -5
  62. package/lib/module/useFocusEvents.js.map +1 -1
  63. package/lib/module/useFocusedListenersChildrenAdapter.js +3 -3
  64. package/lib/module/useFocusedListenersChildrenAdapter.js.map +1 -1
  65. package/lib/module/useIsFocused.js +8 -12
  66. package/lib/module/useIsFocused.js.map +1 -1
  67. package/lib/module/useKeyedChildListeners.js +5 -2
  68. package/lib/module/useKeyedChildListeners.js.map +1 -1
  69. package/lib/module/useLazyValue.js.map +1 -1
  70. package/lib/module/useNavigation.js +4 -17
  71. package/lib/module/useNavigation.js.map +1 -1
  72. package/lib/module/useNavigationBuilder.js +244 -147
  73. package/lib/module/useNavigationBuilder.js.map +1 -1
  74. package/lib/module/useNavigationCache.js +92 -89
  75. package/lib/module/useNavigationCache.js.map +1 -1
  76. package/lib/module/useNavigationHelpers.js +22 -29
  77. package/lib/module/useNavigationHelpers.js.map +1 -1
  78. package/lib/module/useNavigationIndependentTree.js +1 -1
  79. package/lib/module/useNavigationIndependentTree.js.map +1 -1
  80. package/lib/module/useNavigationState.js +158 -46
  81. package/lib/module/useNavigationState.js.map +1 -1
  82. package/lib/module/useOnAction.js +58 -11
  83. package/lib/module/useOnAction.js.map +1 -1
  84. package/lib/module/useOnGetNavigation.js +26 -0
  85. package/lib/module/useOnGetNavigation.js.map +1 -0
  86. package/lib/module/useOnGetState.js +7 -4
  87. package/lib/module/useOnGetState.js.map +1 -1
  88. package/lib/module/useOnPreventRemove.js +40 -31
  89. package/lib/module/useOnPreventRemove.js.map +1 -1
  90. package/lib/module/useOnRouteFocus.js +2 -2
  91. package/lib/module/useOnRouteFocus.js.map +1 -1
  92. package/lib/module/useOptionsGetters.js +34 -24
  93. package/lib/module/useOptionsGetters.js.map +1 -1
  94. package/lib/module/usePreventRemove.js +11 -5
  95. package/lib/module/usePreventRemove.js.map +1 -1
  96. package/lib/module/usePreventRemoveContext.js +2 -2
  97. package/lib/module/usePreventRemoveContext.js.map +1 -1
  98. package/lib/module/useRegisterNavigator.js +3 -4
  99. package/lib/module/useRegisterNavigator.js.map +1 -1
  100. package/lib/module/useRoute.js +2 -0
  101. package/lib/module/useRoute.js.map +1 -1
  102. package/lib/module/useRouteCache.js +51 -35
  103. package/lib/module/useRouteCache.js.map +1 -1
  104. package/lib/module/useScheduleUpdate.js +12 -10
  105. package/lib/module/useScheduleUpdate.js.map +1 -1
  106. package/lib/module/useStateForPath.js +1 -1
  107. package/lib/module/useStateForPath.js.map +1 -1
  108. package/lib/module/useSyncState.js +37 -15
  109. package/lib/module/useSyncState.js.map +1 -1
  110. package/lib/module/validatePathConfig.js +50 -36
  111. package/lib/module/validatePathConfig.js.map +1 -1
  112. package/lib/typescript/src/BaseNavigationContainer.d.ts +6 -3
  113. package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
  114. package/lib/typescript/src/ConsumedParamsContext.d.ts +3 -0
  115. package/lib/typescript/src/ConsumedParamsContext.d.ts.map +1 -0
  116. package/lib/typescript/src/DataLoading.d.ts +21 -0
  117. package/lib/typescript/src/DataLoading.d.ts.map +1 -0
  118. package/lib/typescript/src/EnsureSingleNavigator.d.ts +1 -1
  119. package/lib/typescript/src/EnsureSingleNavigator.d.ts.map +1 -1
  120. package/lib/typescript/src/Group.d.ts +1 -1
  121. package/lib/typescript/src/Group.d.ts.map +1 -1
  122. package/lib/typescript/src/NavigationBuilderContext.d.ts +22 -14
  123. package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
  124. package/lib/typescript/src/NavigationContainerRefContext.d.ts +1 -1
  125. package/lib/typescript/src/NavigationContainerRefContext.d.ts.map +1 -1
  126. package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts +4 -4
  127. package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts.map +1 -1
  128. package/lib/typescript/src/NavigationHelpersContext.d.ts +1 -1
  129. package/lib/typescript/src/NavigationHelpersContext.d.ts.map +1 -1
  130. package/lib/typescript/src/NavigationIndependentTree.d.ts +1 -1
  131. package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
  132. package/lib/typescript/src/NavigationProvider.d.ts +21 -18
  133. package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
  134. package/lib/typescript/src/NavigationRootContext.d.ts +8 -0
  135. package/lib/typescript/src/NavigationRootContext.d.ts.map +1 -0
  136. package/lib/typescript/src/NavigationStateContext.d.ts +4 -3
  137. package/lib/typescript/src/NavigationStateContext.d.ts.map +1 -1
  138. package/lib/typescript/src/PreventRemoveContext.d.ts +1 -0
  139. package/lib/typescript/src/PreventRemoveContext.d.ts.map +1 -1
  140. package/lib/typescript/src/PreventRemoveProvider.d.ts +1 -1
  141. package/lib/typescript/src/PreventRemoveProvider.d.ts.map +1 -1
  142. package/lib/typescript/src/SceneView.d.ts +5 -2
  143. package/lib/typescript/src/SceneView.d.ts.map +1 -1
  144. package/lib/typescript/src/Screen.d.ts +1 -1
  145. package/lib/typescript/src/Screen.d.ts.map +1 -1
  146. package/lib/typescript/src/StaticContainer.d.ts +1 -1
  147. package/lib/typescript/src/StaticContainer.d.ts.map +1 -1
  148. package/lib/typescript/src/StaticNavigation.d.ts +189 -111
  149. package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
  150. package/lib/typescript/src/StaticTreeContext.d.ts +4 -0
  151. package/lib/typescript/src/StaticTreeContext.d.ts.map +1 -0
  152. package/lib/typescript/src/createNavigationContainerRef.d.ts +1 -1
  153. package/lib/typescript/src/createNavigationContainerRef.d.ts.map +1 -1
  154. package/lib/typescript/src/createNavigatorFactory.d.ts +23 -5
  155. package/lib/typescript/src/createNavigatorFactory.d.ts.map +1 -1
  156. package/lib/typescript/src/findFocusedRoute.d.ts +3 -8
  157. package/lib/typescript/src/findFocusedRoute.d.ts.map +1 -1
  158. package/lib/typescript/src/getActionFromState.d.ts +4 -4
  159. package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
  160. package/lib/typescript/src/getPathFromState.d.ts +3 -3
  161. package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
  162. package/lib/typescript/src/getPatternParts.d.ts +5 -2
  163. package/lib/typescript/src/getPatternParts.d.ts.map +1 -1
  164. package/lib/typescript/src/getStateFromPath.d.ts +4 -7
  165. package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
  166. package/lib/typescript/src/getStateFromRouteParams.d.ts +3 -0
  167. package/lib/typescript/src/getStateFromRouteParams.d.ts.map +1 -0
  168. package/lib/typescript/src/index.d.ts +36 -33
  169. package/lib/typescript/src/index.d.ts.map +1 -1
  170. package/lib/typescript/src/internal.d.ts +3 -0
  171. package/lib/typescript/src/internal.d.ts.map +1 -0
  172. package/lib/typescript/src/isRecordEqual.d.ts.map +1 -1
  173. package/lib/typescript/src/queryString.d.ts +5 -0
  174. package/lib/typescript/src/queryString.d.ts.map +1 -0
  175. package/lib/typescript/src/theming/ThemeContext.d.ts +1 -1
  176. package/lib/typescript/src/theming/ThemeContext.d.ts.map +1 -1
  177. package/lib/typescript/src/theming/ThemeProvider.d.ts +2 -2
  178. package/lib/typescript/src/theming/ThemeProvider.d.ts.map +1 -1
  179. package/lib/typescript/src/theming/useTheme.d.ts +1 -1
  180. package/lib/typescript/src/theming/useTheme.d.ts.map +1 -1
  181. package/lib/typescript/src/types.d.ts +265 -167
  182. package/lib/typescript/src/types.d.ts.map +1 -1
  183. package/lib/typescript/src/useChildListeners.d.ts +3 -3
  184. package/lib/typescript/src/useChildListeners.d.ts.map +1 -1
  185. package/lib/typescript/src/useDescriptors.d.ts +13 -11
  186. package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
  187. package/lib/typescript/src/useEventEmitter.d.ts +2 -2
  188. package/lib/typescript/src/useEventEmitter.d.ts.map +1 -1
  189. package/lib/typescript/src/useFocusEffect.d.ts.map +1 -1
  190. package/lib/typescript/src/useFocusEvents.d.ts +2 -2
  191. package/lib/typescript/src/useFocusEvents.d.ts.map +1 -1
  192. package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts +2 -2
  193. package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts.map +1 -1
  194. package/lib/typescript/src/useIsFocused.d.ts +4 -0
  195. package/lib/typescript/src/useIsFocused.d.ts.map +1 -1
  196. package/lib/typescript/src/useKeyedChildListeners.d.ts +4 -3
  197. package/lib/typescript/src/useKeyedChildListeners.d.ts.map +1 -1
  198. package/lib/typescript/src/useLazyValue.d.ts.map +1 -1
  199. package/lib/typescript/src/useNavigation.d.ts +4 -2
  200. package/lib/typescript/src/useNavigation.d.ts.map +1 -1
  201. package/lib/typescript/src/useNavigationBuilder.d.ts +22 -63
  202. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  203. package/lib/typescript/src/useNavigationCache.d.ts +6 -5
  204. package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
  205. package/lib/typescript/src/useNavigationContainerRef.d.ts +1 -1
  206. package/lib/typescript/src/useNavigationContainerRef.d.ts.map +1 -1
  207. package/lib/typescript/src/useNavigationHelpers.d.ts +18 -58
  208. package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
  209. package/lib/typescript/src/useNavigationState.d.ts +19 -3
  210. package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
  211. package/lib/typescript/src/useOnAction.d.ts +9 -9
  212. package/lib/typescript/src/useOnAction.d.ts.map +1 -1
  213. package/lib/typescript/src/useOnGetNavigation.d.ts +12 -0
  214. package/lib/typescript/src/useOnGetNavigation.d.ts.map +1 -0
  215. package/lib/typescript/src/useOnGetState.d.ts +1 -1
  216. package/lib/typescript/src/useOnGetState.d.ts.map +1 -1
  217. package/lib/typescript/src/useOnPreventRemove.d.ts +5 -9
  218. package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
  219. package/lib/typescript/src/useOnRouteFocus.d.ts +6 -6
  220. package/lib/typescript/src/useOnRouteFocus.d.ts.map +1 -1
  221. package/lib/typescript/src/useOptionsGetters.d.ts +2 -4
  222. package/lib/typescript/src/useOptionsGetters.d.ts.map +1 -1
  223. package/lib/typescript/src/usePreventRemove.d.ts.map +1 -1
  224. package/lib/typescript/src/usePreventRemoveContext.d.ts +2 -1
  225. package/lib/typescript/src/usePreventRemoveContext.d.ts.map +1 -1
  226. package/lib/typescript/src/useRegisterNavigator.d.ts.map +1 -1
  227. package/lib/typescript/src/useRoute.d.ts +7 -3
  228. package/lib/typescript/src/useRoute.d.ts.map +1 -1
  229. package/lib/typescript/src/useRouteCache.d.ts +7 -12
  230. package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
  231. package/lib/typescript/src/useScheduleUpdate.d.ts +5 -4
  232. package/lib/typescript/src/useScheduleUpdate.d.ts.map +1 -1
  233. package/lib/typescript/src/useStateForPath.d.ts +1 -1
  234. package/lib/typescript/src/useStateForPath.d.ts.map +1 -1
  235. package/lib/typescript/src/useSyncState.d.ts +1 -0
  236. package/lib/typescript/src/useSyncState.d.ts.map +1 -1
  237. package/lib/typescript/src/utilities.d.ts +79 -39
  238. package/lib/typescript/src/utilities.d.ts.map +1 -1
  239. package/lib/typescript/src/validatePathConfig.d.ts.map +1 -1
  240. package/package.json +26 -22
  241. package/src/BaseNavigationContainer.tsx +468 -333
  242. package/src/ConsumedParamsContext.tsx +5 -0
  243. package/src/DataLoading.tsx +206 -0
  244. package/src/NavigationBuilderContext.tsx +44 -25
  245. package/src/NavigationFocusedRouteStateContext.tsx +4 -4
  246. package/src/NavigationIndependentTree.tsx +60 -12
  247. package/src/NavigationProvider.tsx +47 -20
  248. package/src/NavigationRootContext.tsx +11 -0
  249. package/src/NavigationStateContext.tsx +9 -6
  250. package/src/PreventRemoveContext.tsx +1 -0
  251. package/src/PreventRemoveProvider.tsx +55 -56
  252. package/src/SceneView.tsx +14 -40
  253. package/src/StaticNavigation.tsx +715 -328
  254. package/src/StaticTreeContext.tsx +7 -0
  255. package/src/createNavigationContainerRef.tsx +12 -11
  256. package/src/createNavigatorFactory.tsx +92 -8
  257. package/src/findFocusedRoute.tsx +16 -16
  258. package/src/getActionFromState.tsx +87 -51
  259. package/src/getPathFromState.tsx +310 -134
  260. package/src/getPatternParts.tsx +98 -16
  261. package/src/getStateFromPath.tsx +971 -344
  262. package/src/getStateFromRouteParams.tsx +59 -0
  263. package/src/index.tsx +57 -6
  264. package/src/internal.tsx +2 -0
  265. package/src/isRecordEqual.tsx +3 -1
  266. package/src/queryString.ts +109 -0
  267. package/src/theming/useTheme.tsx +1 -1
  268. package/src/types.tsx +799 -377
  269. package/src/useDescriptors.tsx +151 -43
  270. package/src/useEventEmitter.tsx +53 -39
  271. package/src/useFocusEffect.tsx +5 -2
  272. package/src/useFocusEvents.tsx +19 -8
  273. package/src/useFocusedListenersChildrenAdapter.tsx +3 -3
  274. package/src/useIsFocused.tsx +18 -21
  275. package/src/useKeyedChildListeners.tsx +5 -2
  276. package/src/useLazyValue.tsx +2 -2
  277. package/src/useNavigation.tsx +9 -26
  278. package/src/useNavigationBuilder.tsx +370 -201
  279. package/src/useNavigationCache.tsx +148 -123
  280. package/src/useNavigationHelpers.tsx +27 -37
  281. package/src/useNavigationIndependentTree.tsx +1 -1
  282. package/src/useNavigationState.tsx +207 -69
  283. package/src/useOnAction.tsx +92 -18
  284. package/src/useOnGetNavigation.tsx +30 -0
  285. package/src/useOnGetState.tsx +8 -4
  286. package/src/useOnPreventRemove.tsx +78 -43
  287. package/src/useOnRouteFocus.tsx +11 -15
  288. package/src/useOptionsGetters.tsx +41 -28
  289. package/src/usePreventRemove.tsx +13 -5
  290. package/src/usePreventRemoveContext.tsx +2 -2
  291. package/src/useRegisterNavigator.tsx +4 -4
  292. package/src/useRoute.tsx +20 -6
  293. package/src/useRouteCache.tsx +57 -35
  294. package/src/useScheduleUpdate.tsx +13 -11
  295. package/src/useStateForPath.tsx +1 -1
  296. package/src/useSyncState.tsx +39 -15
  297. package/src/utilities.tsx +151 -35
  298. package/src/validatePathConfig.tsx +86 -57
  299. package/lib/module/CurrentRenderContext.js +0 -10
  300. package/lib/module/CurrentRenderContext.js.map +0 -1
  301. package/lib/module/NavigationMetaContext.js +0 -15
  302. package/lib/module/NavigationMetaContext.js.map +0 -1
  303. package/lib/module/checkSerializable.js +0 -46
  304. package/lib/module/checkSerializable.js.map +0 -1
  305. package/lib/module/useComponent.js +0 -34
  306. package/lib/module/useComponent.js.map +0 -1
  307. package/lib/module/useCurrentRender.js +0 -19
  308. package/lib/module/useCurrentRender.js.map +0 -1
  309. package/lib/typescript/src/CurrentRenderContext.d.ts +0 -9
  310. package/lib/typescript/src/CurrentRenderContext.d.ts.map +0 -1
  311. package/lib/typescript/src/NavigationMetaContext.d.ts +0 -12
  312. package/lib/typescript/src/NavigationMetaContext.d.ts.map +0 -1
  313. package/lib/typescript/src/checkSerializable.d.ts +0 -10
  314. package/lib/typescript/src/checkSerializable.d.ts.map +0 -1
  315. package/lib/typescript/src/useComponent.d.ts +0 -7
  316. package/lib/typescript/src/useComponent.d.ts.map +0 -1
  317. package/lib/typescript/src/useCurrentRender.d.ts +0 -14
  318. package/lib/typescript/src/useCurrentRender.d.ts.map +0 -1
  319. package/src/CurrentRenderContext.tsx +0 -9
  320. package/src/NavigationMetaContext.tsx +0 -14
  321. package/src/checkSerializable.tsx +0 -74
  322. package/src/useComponent.tsx +0 -37
  323. package/src/useCurrentRender.tsx +0 -31
@@ -1,18 +1,20 @@
1
1
  import type { NavigationState, ParamListBase } from '@react-navigation/routers';
2
2
  import * as React from 'react';
3
- import useLatestCallback from 'use-latest-callback';
4
- import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector';
5
3
 
6
- import type {
7
- NavigationListForNested,
8
- NavigationProp,
9
- RootNavigator,
10
- } from './types';
11
- import { useNavigation } from './useNavigation';
4
+ import type { NavigationListForNested, RootNavigator } from './types';
5
+
6
+ type NavigationStateEvent = 'update' | 'commit';
12
7
 
13
8
  type NavigationStateListener = {
9
+ initialStateContext: React.Context<NavigationState<ParamListBase>>;
14
10
  getState: () => NavigationState<ParamListBase>;
15
- subscribe: (callback: () => void) => () => void;
11
+ getSnapshot: () => NavigationState<ParamListBase>;
12
+ subscribe: (callback: (event: NavigationStateEvent) => void) => () => void;
13
+ };
14
+
15
+ type NavigationStateEntry = {
16
+ listener: NavigationStateListener;
17
+ state: NavigationState<ParamListBase>;
16
18
  };
17
19
 
18
20
  type NavigationStateForNested<
@@ -35,8 +37,8 @@ type NavigationStateForNested<
35
37
  export function useNavigationState<
36
38
  const T,
37
39
  const Navigator = RootNavigator,
38
- const RouteName extends
39
- keyof NavigationListForNested<Navigator> = keyof NavigationListForNested<Navigator>,
40
+ const RouteName extends keyof NavigationListForNested<Navigator> =
41
+ keyof NavigationListForNested<Navigator>,
40
42
  >(
41
43
  routeName: RouteName,
42
44
  selector: (state: NavigationStateForNested<Navigator, RouteName>) => T
@@ -45,108 +47,244 @@ export function useNavigationState<T>(
45
47
  selector: (state: NavigationState<ParamListBase>) => T
46
48
  ): T;
47
49
  export function useNavigationState(...args: unknown[]): unknown {
48
- let navigation: NavigationProp<ParamListBase> | undefined,
49
- stateListener: NavigationStateListener | undefined,
50
- selector;
50
+ const selector = typeof args[0] === 'string' ? args[1] : args[0];
51
51
 
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];
52
+ if (typeof selector !== 'function') {
53
+ throw new Error(
54
+ `A selector function must be provided (got ${typeof selector}).`
55
+ );
60
56
  }
61
57
 
62
- if (navigation == null) {
63
- stateListener = React.use(NavigationStateListenerContext);
58
+ // @ts-expect-error: the public overloads guarantee the selector's type, but
59
+ // the `unknown[]` implementation signature can't express it
60
+ const select: (state: NavigationState<ParamListBase>) => unknown = selector;
61
+
62
+ let listener: NavigationStateListener | undefined;
63
+
64
+ if (typeof args[0] === 'string') {
65
+ const name = args[0];
66
+ const listeners = React.use(NamedNavigationStateListenerListContext);
67
+
68
+ listener = listeners?.[name];
69
+
70
+ if (listener == null) {
71
+ throw new Error(
72
+ `Couldn't find a navigator for the route '${name}' in any of the parent screens. Is your component inside the correct screen?`
73
+ );
74
+ }
75
+ } else {
76
+ listener = React.use(NavigationStateListenerContext);
64
77
 
65
- if (stateListener == null) {
78
+ if (listener == null) {
66
79
  throw new Error(
67
80
  "Couldn't get the navigation state. Is your component inside a navigator?"
68
81
  );
69
82
  }
70
83
  }
71
84
 
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
- );
85
+ const { getState, getSnapshot, subscribe } = listener;
86
+
87
+ const initialRef = React.useRef(true);
88
+
89
+ // We read the initial state from context on mount instead of using getState
90
+ // This ensures that we get proper state for the current render,
91
+ // even if the external store has been updated.
92
+ // We conditionally use `use` to avoid more unnecessary re-renders after first mount.
93
+ // The context subscription is removed on the next render, which can cause one extra render.
94
+ const initialState = initialRef.current
95
+ ? React.use(listener.initialStateContext)
96
+ : undefined;
97
+
98
+ // We track the last committed reducer entry, navigation state, and selector,
99
+ // so we can check if an update changes the value the component last committed.
100
+ const committedRef = React.useRef<{
101
+ entry: NavigationStateEntry | null;
102
+ state: NavigationState<ParamListBase>;
103
+ select: (state: NavigationState<ParamListBase>) => unknown;
104
+ }>({ entry: null, state: initialState ?? getSnapshot(), select });
105
+
106
+ // The store can contain state from a transition that hasn't committed yet.
107
+ // Reading it during render can show state that doesn't match the navigator in that render.
108
+ // The reducer lets React process state in the same render lane as the navigator.
109
+ const [entry, dispatch] = React.useReducer(
110
+ (previous: NavigationStateEntry, event: NavigationStateEvent) => {
111
+ // Navigation state can change between the dispatch and the reducer running.
112
+ // Reading here ensures the processed event uses the state for that render.
113
+ const state = event === 'update' ? getState() : getSnapshot();
114
+
115
+ // If we're processing the same reducer entry as the last commit,
116
+ // we compare against the state last committed by the component.
117
+ // We keep the reducer entry to avoid re-renders for unchanged selections.
118
+ // This means the reducer entry can still hold older navigation state.
119
+ // If a parent causes another render,
120
+ // we use the latest snapshot so the selector doesn't read stale state.
121
+ // We leave the reducer entry unchanged to avoid an extra render.
122
+ // We save that snapshot in committedRef when the render commits,
123
+ // so later updates compare against the state we actually rendered,
124
+ // including when the selector changed.
125
+ // If the reducer entry is different, we use its own navigation state,
126
+ // so comparisons include updates already processed in the pending render.
127
+ const previousState =
128
+ previous === committedRef.current.entry
129
+ ? committedRef.current.state
130
+ : previous.state;
131
+
132
+ if (
133
+ previous.listener === listener &&
134
+ (previousState === state ||
135
+ (event === 'update' &&
136
+ Object.is(select(previousState), select(state))))
137
+ ) {
138
+ return previous;
82
139
  }
140
+
141
+ return { listener, state };
83
142
  },
84
- [navigation, stateListener]
143
+ undefined,
144
+ () => ({ listener, state: initialState ?? getSnapshot() })
85
145
  );
86
146
 
87
- const getSnapshot = navigation
88
- ? navigation.getState
89
- : stateListener?.getState;
147
+ const state =
148
+ // A different listener means the state belongs to another navigator.
149
+ // This can happen when the passed route name changes.
150
+ entry.listener !== listener ||
151
+ // An entry that advanced past the last commit is at least as new as the snapshot.
152
+ // An entry that didn't advance while the committed state changed is older.
153
+ entry === committedRef.current.entry
154
+ ? getSnapshot()
155
+ : entry.state;
90
156
 
91
- if (getSnapshot == null) {
92
- throw new Error("Couldn't get the navigation state. This is not expected.");
93
- }
157
+ React.useInsertionEffect(() => {
158
+ initialRef.current = false;
159
+ committedRef.current = { entry, state, select };
160
+ });
94
161
 
95
- if (typeof selector !== 'function') {
96
- throw new Error(
97
- `A selector function must be provided (got ${typeof selector}).`
98
- );
99
- }
162
+ React.useLayoutEffect(() => {
163
+ const check = (event: NavigationStateEvent) => {
164
+ const { state, select } = committedRef.current;
100
165
 
101
- const value = useSyncExternalStoreWithSelector(
102
- subscribe,
103
- getSnapshot,
104
- getSnapshot,
105
- // @ts-expect-error we can't infer the type here
106
- selector
107
- );
166
+ // An 'update' event follows the latest state in the lane the update was made in.
167
+ // A 'commit' event syncs the reducer to the state committed by the navigator.
168
+ const target = event === 'update' ? getState() : getSnapshot();
169
+
170
+ if (state !== target && !Object.is(select(state), select(target))) {
171
+ dispatch(event);
172
+ }
173
+ };
174
+
175
+ const unsubscribe = subscribe(check);
176
+
177
+ // The reducer may not match the committed state when the subscription starts.
178
+ // This happens on mount during a transition or an update before subscribing.
179
+ check('commit');
180
+
181
+ return unsubscribe;
182
+ }, [getState, getSnapshot, subscribe]);
108
183
 
109
- return value;
184
+ return select(state);
110
185
  }
111
186
 
112
187
  export function NavigationStateListenerProvider({
188
+ isSynced,
113
189
  state,
190
+ getState,
191
+ subscribe,
114
192
  children,
115
193
  }: {
194
+ isSynced: boolean;
116
195
  state: NavigationState<ParamListBase>;
196
+ getState: () => NavigationState<ParamListBase>;
197
+ subscribe: (callback: () => void) => () => void;
117
198
  children: React.ReactNode;
118
199
  }) {
119
- const listeners = React.useRef<(() => void)[]>([]);
200
+ const [InitialStateContext] = React.useState(() =>
201
+ React.createContext(state)
202
+ );
120
203
 
121
- const getState = useLatestCallback(() => state);
204
+ const [listeners] = React.useState(
205
+ () => new Set<(event: NavigationStateEvent) => void>()
206
+ );
122
207
 
123
- const subscribe = useLatestCallback((callback: () => void) => {
124
- listeners.current.push(callback);
208
+ React.useLayoutEffect(() => {
209
+ listeners.forEach((listener) => listener('commit'));
210
+ }, [listeners, state]);
125
211
 
126
- return () => {
127
- listeners.current = listeners.current.filter((cb) => cb !== callback);
128
- };
129
- });
212
+ const snapshotRef = React.useRef(state);
130
213
 
131
- React.useEffect(() => {
132
- listeners.current.forEach((callback) => callback());
214
+ React.useInsertionEffect(() => {
215
+ snapshotRef.current = state;
133
216
  }, [state]);
134
217
 
218
+ // Route config or nested params can produce new state during render.
219
+ // If the scheduled update hasn't synced that state to the store yet,
220
+ // We need to expose the rendered state so consumers match the navigator.
221
+ // We keep this `undefined` for other scenarios, so the context isn't recreated.
222
+ const nextState = isSynced ? undefined : state;
223
+
135
224
  const context = React.useMemo(
136
225
  () => ({
226
+ initialStateContext: InitialStateContext,
137
227
  getState,
138
- subscribe,
228
+ getSnapshot: () => nextState ?? snapshotRef.current,
229
+ subscribe: (callback: (event: NavigationStateEvent) => void) => {
230
+ const unsubscribe = subscribe(() => callback('update'));
231
+
232
+ listeners.add(callback);
233
+
234
+ return () => {
235
+ listeners.delete(callback);
236
+ unsubscribe();
237
+ };
238
+ },
139
239
  }),
140
- [getState, subscribe]
240
+ [InitialStateContext, getState, listeners, nextState, subscribe]
141
241
  );
142
242
 
143
243
  return (
144
244
  <NavigationStateListenerContext.Provider value={context}>
145
- {children}
245
+ <InitialStateContext.Provider value={state}>
246
+ {children}
247
+ </InitialStateContext.Provider>
146
248
  </NavigationStateListenerContext.Provider>
147
249
  );
148
250
  }
149
251
 
150
- const NavigationStateListenerContext = React.createContext<
252
+ export function NamedNavigationStateListenerProvider({
253
+ name,
254
+ children,
255
+ }: {
256
+ name: string;
257
+ children: React.ReactNode;
258
+ }) {
259
+ const listener = React.use(NavigationStateListenerContext);
260
+
261
+ if (listener == null) {
262
+ throw new Error(
263
+ "Couldn't find a navigation state listener. 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'."
264
+ );
265
+ }
266
+
267
+ const parents = React.use(NamedNavigationStateListenerListContext);
268
+
269
+ const listeners = React.useMemo(
270
+ () => ({
271
+ ...parents,
272
+ [name]: listener,
273
+ }),
274
+ [listener, name, parents]
275
+ );
276
+
277
+ return (
278
+ <NamedNavigationStateListenerListContext.Provider value={listeners}>
279
+ {children}
280
+ </NamedNavigationStateListenerListContext.Provider>
281
+ );
282
+ }
283
+
284
+ export const NavigationStateListenerContext = React.createContext<
151
285
  NavigationStateListener | undefined
152
286
  >(undefined);
287
+
288
+ export const NamedNavigationStateListenerListContext = React.createContext<
289
+ Record<string, NavigationStateListener> | undefined
290
+ >(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
- NavigationBuilderContext,
15
+ useNavigationBuilderContext,
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
+ } = useNavigationBuilderContext();
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
@@ -83,13 +95,23 @@ export function useOnAction({
83
95
  );
84
96
 
85
97
  // If a target is specified and set to current navigator, the action shouldn't bubble
86
- // So instead of `null`, we use the state object for such cases to signal that action was handled
87
- result =
88
- result === null && action.target === state.key ? state : result;
98
+ // So we immediately return `false` to mark it as unhandled
99
+ if (result === null && action.target === state.key) {
100
+ return false;
101
+ }
89
102
 
90
- if (result !== null) {
91
- onDispatchAction(action, state === result);
103
+ if (result !== null && result.stale !== false) {
104
+ // Some actions (e.g. `RESET`) may return a stale state from the router
105
+ // We rehydrate it before storing so the stored state is always usable
106
+ // Otherwise anything reading the state before the next render
107
+ // (e.g. another dispatch immediately after) would get an outdated state
108
+ result = router.getRehydratedState(
109
+ result,
110
+ routerConfigOptionsRef.current
111
+ );
112
+ }
92
113
 
114
+ if (result !== null) {
93
115
  if (state !== result) {
94
116
  const isPrevented = shouldPreventRemove(
95
117
  emitter,
@@ -100,10 +122,34 @@ export function useOnAction({
100
122
  );
101
123
 
102
124
  if (isPrevented) {
125
+ onDispatchAction(action, true);
126
+
103
127
  return true;
104
128
  }
105
129
 
130
+ if (getState() !== state) {
131
+ // Listeners for 'beforeRemove' may dispatch actions that change the state
132
+ // Then the result we have is outdated and would overwrite those changes
133
+ // So we handle the action again to calculate the result with the latest state
134
+ return onAction(action, new Set<string>());
135
+ }
136
+
137
+ onDispatchAction(action, false);
138
+
139
+ if (tree) {
140
+ const loader = getLoaderForStateChange(
141
+ tree,
142
+ result,
143
+ state,
144
+ consumedParams
145
+ );
146
+
147
+ loader?.();
148
+ }
149
+
106
150
  setState(result);
151
+ } else {
152
+ onDispatchAction(action, true);
107
153
  }
108
154
 
109
155
  if (onRouteFocusParent !== undefined) {
@@ -120,19 +166,44 @@ export function useOnAction({
120
166
  }
121
167
  }
122
168
 
169
+ const isSourceInCurrentNavigator =
170
+ action.source !== undefined &&
171
+ state.routes.some((r) => r.key === action.source);
172
+
123
173
  if (onActionParent !== undefined) {
124
174
  // Bubble action to the parent if the current navigator didn't handle it
125
- if (onActionParent(action, visitedNavigators)) {
175
+ const parentAction =
176
+ isSourceInCurrentNavigator && key !== undefined
177
+ ? {
178
+ ...action,
179
+ // If the `source` belonged to the current navigator,
180
+ // rewrite the `source` to the parent screen's key when bubbling
181
+ // This will let parent handle it relative to the child that dispatched the action
182
+ source: key,
183
+ }
184
+ : action;
185
+
186
+ if (onActionParent(parentAction, visitedNavigators)) {
126
187
  return true;
127
188
  }
128
189
  }
129
190
 
130
191
  if (typeof action.target === 'string') {
131
192
  // If the action wasn't handled by current navigator or a parent navigator, let children handle it
193
+ const childAction = isSourceInCurrentNavigator
194
+ ? {
195
+ ...action,
196
+ // If the `source` belonged to the current navigator,
197
+ // remove it when targeting a child navigator
198
+ // So child navigators can handle it based on the focused screen
199
+ source: undefined,
200
+ }
201
+ : action;
202
+
132
203
  for (let i = actionListeners.length - 1; i >= 0; i--) {
133
204
  const listener = actionListeners[i];
134
205
 
135
- if (listener(action, visitedNavigators)) {
206
+ if (listener?.(childAction, visitedNavigators)) {
136
207
  return true;
137
208
  }
138
209
  }
@@ -143,7 +214,9 @@ export function useOnAction({
143
214
  [
144
215
  actionListeners,
145
216
  beforeRemoveListeners,
217
+ consumedParams,
146
218
  emitter,
219
+ flushUpdates,
147
220
  getState,
148
221
  key,
149
222
  onActionParent,
@@ -151,6 +224,7 @@ export function useOnAction({
151
224
  onRouteFocusParent,
152
225
  router,
153
226
  setState,
227
+ tree,
154
228
  ]
155
229
  );
156
230
 
@@ -160,7 +234,7 @@ export function useOnAction({
160
234
  beforeRemoveListeners,
161
235
  });
162
236
 
163
- React.useEffect(
237
+ React.useInsertionEffect(
164
238
  () => addListenerParent?.('action', onAction),
165
239
  [addListenerParent, onAction]
166
240
  );
@@ -0,0 +1,30 @@
1
+ import type { ParamListBase } from '@react-navigation/routers';
2
+ import * as React from 'react';
3
+
4
+ import { useNavigationBuilderContext } from './NavigationBuilderContext';
5
+ import { NavigationRouteContext } from './NavigationProvider';
6
+ import type { NavigationHelpers } from './types';
7
+
8
+ type Options = {
9
+ navigation: NavigationHelpers<ParamListBase>;
10
+ };
11
+
12
+ /**
13
+ * Hook which lets the container access the navigation object of the root navigator.
14
+ * It's used for the navigation object returned by `useNavigation` outside a screen.
15
+ */
16
+ export function useOnGetNavigation({ navigation }: Options) {
17
+ const { addKeyedListener } = useNavigationBuilderContext();
18
+
19
+ const route = React.use(NavigationRouteContext);
20
+
21
+ React.useInsertionEffect(() => {
22
+ // We only register if this is the root navigator
23
+ // i.e. there is no parent route
24
+ if (route != null) {
25
+ return;
26
+ }
27
+
28
+ return addKeyedListener?.('getNavigation', 'root', () => navigation);
29
+ }, [addKeyedListener, navigation, route]);
30
+ }
@@ -4,7 +4,7 @@ import * as React from 'react';
4
4
  import { isArrayEqual } from './isArrayEqual';
5
5
  import {
6
6
  type GetStateListener,
7
- NavigationBuilderContext,
7
+ useNavigationBuilderContext,
8
8
  } from './NavigationBuilderContext';
9
9
  import { NavigationRouteContext } from './NavigationProvider';
10
10
 
@@ -14,8 +14,10 @@ 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 } = useNavigationBuilderContext();
18
+
19
+ const route = React.use(NavigationRouteContext);
20
+
19
21
  const key = route ? route.key : 'root';
20
22
 
21
23
  const getRehydratedState = React.useCallback(() => {
@@ -39,7 +41,9 @@ export function useOnGetState({ getState, getStateListeners }: Options) {
39
41
  return { ...state, routes };
40
42
  }, [getState, getStateListeners]);
41
43
 
42
- React.useEffect(() => {
44
+ // Use insertion effect to preserve the listener under `<Activity mode="hidden">`
45
+ // So `getRootState` will still return state of nested navigators
46
+ React.useInsertionEffect(() => {
43
47
  return addKeyedListener?.('getState', key, getRehydratedState);
44
48
  }, [addKeyedListener, getRehydratedState, key]);
45
49
  }