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

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 (123) hide show
  1. package/lib/module/BaseNavigationContainer.js +132 -82
  2. package/lib/module/BaseNavigationContainer.js.map +1 -1
  3. package/lib/module/DataLoading.js +3 -1
  4. package/lib/module/DataLoading.js.map +1 -1
  5. package/lib/module/NavigationBuilderContext.js +2 -1
  6. package/lib/module/NavigationBuilderContext.js.map +1 -1
  7. package/lib/module/NavigationIndependentTree.js +21 -11
  8. package/lib/module/NavigationIndependentTree.js.map +1 -1
  9. package/lib/module/NavigationProvider.js +10 -1
  10. package/lib/module/NavigationProvider.js.map +1 -1
  11. package/lib/module/NavigationRootContext.js +8 -0
  12. package/lib/module/NavigationRootContext.js.map +1 -0
  13. package/lib/module/NavigationStateContext.js +3 -0
  14. package/lib/module/NavigationStateContext.js.map +1 -1
  15. package/lib/module/SceneView.js +3 -1
  16. package/lib/module/SceneView.js.map +1 -1
  17. package/lib/module/StaticNavigation.js +54 -19
  18. package/lib/module/StaticNavigation.js.map +1 -1
  19. package/lib/module/createNavigationContainerRef.js +1 -1
  20. package/lib/module/createNavigationContainerRef.js.map +1 -1
  21. package/lib/module/findFocusedRoute.js.map +1 -1
  22. package/lib/module/getActionFromState.js.map +1 -1
  23. package/lib/module/getStateFromPath.js +15 -16
  24. package/lib/module/getStateFromPath.js.map +1 -1
  25. package/lib/module/getStateFromRouteParams.js +0 -1
  26. package/lib/module/getStateFromRouteParams.js.map +1 -1
  27. package/lib/module/index.js +1 -1
  28. package/lib/module/index.js.map +1 -1
  29. package/lib/module/internal.js +2 -0
  30. package/lib/module/internal.js.map +1 -0
  31. package/lib/module/types.js +0 -1
  32. package/lib/module/types.js.map +1 -1
  33. package/lib/module/useDescriptors.js +6 -4
  34. package/lib/module/useDescriptors.js.map +1 -1
  35. package/lib/module/useKeyedChildListeners.js +1 -0
  36. package/lib/module/useKeyedChildListeners.js.map +1 -1
  37. package/lib/module/useNavigation.js +2 -2
  38. package/lib/module/useNavigation.js.map +1 -1
  39. package/lib/module/useNavigationBuilder.js +14 -4
  40. package/lib/module/useNavigationBuilder.js.map +1 -1
  41. package/lib/module/useNavigationCache.js +23 -31
  42. package/lib/module/useNavigationCache.js.map +1 -1
  43. package/lib/module/useNavigationHelpers.js +18 -37
  44. package/lib/module/useNavigationHelpers.js.map +1 -1
  45. package/lib/module/useNavigationState.js +85 -33
  46. package/lib/module/useNavigationState.js.map +1 -1
  47. package/lib/module/useOnGetNavigation.js +26 -0
  48. package/lib/module/useOnGetNavigation.js.map +1 -0
  49. package/lib/module/useSyncState.js +25 -6
  50. package/lib/module/useSyncState.js.map +1 -1
  51. package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
  52. package/lib/typescript/src/DataLoading.d.ts.map +1 -1
  53. package/lib/typescript/src/NavigationBuilderContext.d.ts +4 -1
  54. package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
  55. package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
  56. package/lib/typescript/src/NavigationProvider.d.ts +9 -5
  57. package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
  58. package/lib/typescript/src/NavigationRootContext.d.ts +8 -0
  59. package/lib/typescript/src/NavigationRootContext.d.ts.map +1 -0
  60. package/lib/typescript/src/NavigationStateContext.d.ts +1 -0
  61. package/lib/typescript/src/NavigationStateContext.d.ts.map +1 -1
  62. package/lib/typescript/src/SceneView.d.ts +2 -1
  63. package/lib/typescript/src/SceneView.d.ts.map +1 -1
  64. package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
  65. package/lib/typescript/src/createNavigationContainerRef.d.ts.map +1 -1
  66. package/lib/typescript/src/findFocusedRoute.d.ts +3 -8
  67. package/lib/typescript/src/findFocusedRoute.d.ts.map +1 -1
  68. package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
  69. package/lib/typescript/src/getStateFromPath.d.ts +1 -4
  70. package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
  71. package/lib/typescript/src/getStateFromRouteParams.d.ts.map +1 -1
  72. package/lib/typescript/src/index.d.ts +1 -1
  73. package/lib/typescript/src/index.d.ts.map +1 -1
  74. package/lib/typescript/src/internal.d.ts +3 -0
  75. package/lib/typescript/src/internal.d.ts.map +1 -0
  76. package/lib/typescript/src/types.d.ts +3 -6
  77. package/lib/typescript/src/types.d.ts.map +1 -1
  78. package/lib/typescript/src/useDescriptors.d.ts +2 -1
  79. package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
  80. package/lib/typescript/src/useKeyedChildListeners.d.ts +1 -0
  81. package/lib/typescript/src/useKeyedChildListeners.d.ts.map +1 -1
  82. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  83. package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
  84. package/lib/typescript/src/useNavigationHelpers.d.ts +1 -2
  85. package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
  86. package/lib/typescript/src/useNavigationState.d.ts +7 -2
  87. package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
  88. package/lib/typescript/src/useOnGetNavigation.d.ts +12 -0
  89. package/lib/typescript/src/useOnGetNavigation.d.ts.map +1 -0
  90. package/lib/typescript/src/useSyncState.d.ts +1 -0
  91. package/lib/typescript/src/useSyncState.d.ts.map +1 -1
  92. package/package.json +11 -5
  93. package/src/BaseNavigationContainer.tsx +194 -97
  94. package/src/DataLoading.tsx +4 -1
  95. package/src/NavigationBuilderContext.tsx +10 -1
  96. package/src/NavigationIndependentTree.tsx +27 -16
  97. package/src/NavigationProvider.tsx +13 -6
  98. package/src/NavigationRootContext.tsx +11 -0
  99. package/src/NavigationStateContext.tsx +4 -0
  100. package/src/SceneView.tsx +4 -0
  101. package/src/StaticNavigation.tsx +77 -19
  102. package/src/createNavigationContainerRef.tsx +0 -2
  103. package/src/findFocusedRoute.tsx +7 -12
  104. package/src/getActionFromState.tsx +2 -1
  105. package/src/getStateFromPath.tsx +31 -43
  106. package/src/getStateFromRouteParams.tsx +0 -1
  107. package/src/index.tsx +1 -0
  108. package/src/internal.tsx +2 -0
  109. package/src/types.tsx +3 -7
  110. package/src/useDescriptors.tsx +6 -3
  111. package/src/useKeyedChildListeners.tsx +1 -0
  112. package/src/useNavigation.tsx +2 -2
  113. package/src/useNavigationBuilder.tsx +14 -4
  114. package/src/useNavigationCache.tsx +24 -33
  115. package/src/useNavigationHelpers.tsx +19 -38
  116. package/src/useNavigationState.tsx +104 -31
  117. package/src/useOnGetNavigation.tsx +29 -0
  118. package/src/useSyncState.tsx +27 -6
  119. package/lib/module/checkSerializable.js +0 -51
  120. package/lib/module/checkSerializable.js.map +0 -1
  121. package/lib/typescript/src/checkSerializable.d.ts +0 -14
  122. package/lib/typescript/src/checkSerializable.d.ts.map +0 -1
  123. package/src/checkSerializable.tsx +0 -90
@@ -678,6 +678,9 @@ const MemoizedScreen = React.memo(
678
678
 
679
679
  MemoizedScreen.displayName = 'Memo(Screen)';
680
680
 
681
+ const hasPropertyGetter = (object: object, property: PropertyKey) =>
682
+ Object.getOwnPropertyDescriptor(object, property)?.get != null;
683
+
681
684
  const getItemsFromScreens = (
682
685
  Screen: React.ComponentType<any>,
683
686
  screens: StaticConfigScreens<any, any, any, any, any>
@@ -688,18 +691,57 @@ const getItemsFromScreens = (
688
691
  let useIf: (() => boolean) | undefined;
689
692
 
690
693
  let isNavigator = false;
694
+ let getComponent: (() => React.ComponentType<any>) | undefined;
691
695
 
692
696
  if ('screen' in item) {
693
- const { screen, if: _if, ...rest } = item;
697
+ if (hasPropertyGetter(item, 'screen')) {
698
+ const rest: Record<string, unknown> = {};
699
+
700
+ // Iterate over the keys to avoid triggering the getter for 'screen'
701
+ for (const key of Object.keys(item)) {
702
+ if (key === 'screen' || key === 'if') {
703
+ continue;
704
+ }
705
+
706
+ rest[key] = Reflect.get(item, key);
707
+ }
708
+
709
+ useIf = item.if;
710
+ props = rest;
694
711
 
695
- useIf = _if;
696
- props = rest;
712
+ getComponent = () => {
713
+ const screen = item.screen;
697
714
 
698
- if (isValidElementType(screen)) {
699
- component = screen;
700
- } else if ('config' in screen) {
701
- isNavigator = true;
702
- component = screen.getComponent();
715
+ if (isValidElementType(screen)) {
716
+ return screen;
717
+ }
718
+
719
+ if (
720
+ screen != null &&
721
+ typeof screen === 'object' &&
722
+ 'config' in screen
723
+ ) {
724
+ throw new Error(
725
+ `Got a navigator object for the screen '${name}' instead of a component. A navigator cannot be specified when using a getter.`
726
+ );
727
+ }
728
+
729
+ throw new Error(
730
+ `Got an invalid screen component for the screen '${name}'. This can happen if you passed 'undefined'. You likely forgot to export your component from the file it's defined in, or mixed up default import and named import when importing.`
731
+ );
732
+ };
733
+ } else {
734
+ const { screen, if: _if, ...rest } = item;
735
+
736
+ useIf = _if;
737
+ props = rest;
738
+
739
+ if (isValidElementType(screen)) {
740
+ component = screen;
741
+ } else if ('config' in screen) {
742
+ isNavigator = true;
743
+ component = screen.getComponent();
744
+ }
703
745
  }
704
746
  } else if (isValidElementType(item)) {
705
747
  component = item;
@@ -708,18 +750,29 @@ const getItemsFromScreens = (
708
750
  component = item.getComponent();
709
751
  }
710
752
 
711
- if (component == null) {
753
+ let children: (() => React.JSX.Element) | undefined;
754
+
755
+ if (getComponent != null) {
756
+ let element: React.JSX.Element | undefined;
757
+
758
+ children = () =>
759
+ (element ??= <MemoizedScreen component={getComponent()} />);
760
+ } else if (component != null) {
761
+ const element = isNavigator ? (
762
+ React.createElement(component, {})
763
+ ) : (
764
+ <MemoizedScreen component={component} />
765
+ );
766
+
767
+ children = () => element;
768
+ }
769
+
770
+ if (children == null) {
712
771
  throw new Error(
713
772
  `Couldn't find a 'screen' property for the screen '${name}'. This can happen if you passed 'undefined'. You likely forgot to export your component from the file it's defined in, or mixed up default import and named import when importing.`
714
773
  );
715
774
  }
716
775
 
717
- const element = isNavigator ? (
718
- React.createElement(component, {})
719
- ) : (
720
- <MemoizedScreen component={component} />
721
- );
722
-
723
776
  return () => {
724
777
  const shouldRender = useIf == null || useIf();
725
778
 
@@ -729,7 +782,7 @@ const getItemsFromScreens = (
729
782
 
730
783
  return (
731
784
  <Screen key={name} name={name} {...props}>
732
- {() => element}
785
+ {children}
733
786
  </Screen>
734
787
  );
735
788
  };
@@ -895,7 +948,7 @@ type PathConfigForStaticNavigation = Omit<
895
948
  // Mark screens that resolve to the same pattern and same component or navigator
896
949
  const markSharedPathConfigs = (
897
950
  screens: PathConfigMapForStaticNavigation,
898
- sources: WeakMap<PathConfigForStaticNavigation, ScreenValueForPathConfig>,
951
+ sources: WeakMap<PathConfigForStaticNavigation, ScreenForPathConfig>,
899
952
  blocked: WeakSet<PathConfigForStaticNavigation>
900
953
  ) => {
901
954
  const candidates = new Map<string, PathConfigForStaticNavigation[]>();
@@ -992,7 +1045,7 @@ export function createPathConfigForStaticNavigation<ParamList extends {}>(
992
1045
  const blocked = new WeakSet<PathConfigForStaticNavigation>();
993
1046
  const sources = new WeakMap<
994
1047
  PathConfigForStaticNavigation,
995
- ScreenValueForPathConfig
1048
+ ScreenForPathConfig
996
1049
  >();
997
1050
 
998
1051
  const createPathConfigForTree = (
@@ -1043,7 +1096,11 @@ export function createPathConfigForStaticNavigation<ParamList extends {}>(
1043
1096
 
1044
1097
  sources.set(
1045
1098
  screenConfig,
1046
- typeof item === 'object' && 'screen' in item ? item.screen : item
1099
+ typeof item === 'object' &&
1100
+ 'screen' in item &&
1101
+ !hasPropertyGetter(item, 'screen')
1102
+ ? item.screen
1103
+ : item
1047
1104
  );
1048
1105
 
1049
1106
  const normalizePath = (path: string) =>
@@ -1129,6 +1186,7 @@ export function createPathConfigForStaticNavigation<ParamList extends {}>(
1129
1186
  !hasExplicitScreens &&
1130
1187
  !hasDisabledLinking &&
1131
1188
  'screen' in item &&
1189
+ !hasPropertyGetter(item, 'screen') &&
1132
1190
  'config' in item.screen &&
1133
1191
  (item.screen.config.screens || item.screen.config.groups)
1134
1192
  ) {
@@ -19,11 +19,9 @@ export function createNavigationContainerRef<
19
19
  'removeListener',
20
20
  'resetRoot',
21
21
  'dispatch',
22
- 'isFocused',
23
22
  'canGoBack',
24
23
  'getRootState',
25
24
  'getState',
26
- 'getParent',
27
25
  'getCurrentRoute',
28
26
  'getCurrentOptions',
29
27
  ] as const;
@@ -1,19 +1,14 @@
1
- import type { InitialState } from '@react-navigation/routers';
1
+ import type { NavigationState, PartialState } from '@react-navigation/routers';
2
2
 
3
- type Result =
4
- | {
5
- key?: string | undefined;
6
- name: string;
7
- params?: object | undefined;
8
- path?: string | undefined;
9
- }
10
- | undefined;
3
+ type State = NavigationState | PartialState<NavigationState>;
11
4
 
12
- export function findFocusedRoute(state: InitialState): Result {
13
- let current: InitialState | undefined = state;
5
+ export function findFocusedRoute(
6
+ state: State
7
+ ): State['routes'][number] | undefined {
8
+ let current: State | undefined = state;
14
9
 
15
10
  while (current != null) {
16
- const route: InitialState['routes'][number] | undefined =
11
+ const route: State['routes'][number] | undefined =
17
12
  current.routes[current.index ?? current.routes.length - 1];
18
13
 
19
14
  if (route?.state == null) {
@@ -89,7 +89,8 @@ export function getActionFromState(
89
89
  return undefined;
90
90
  }
91
91
 
92
- let current: PartialState<NavigationState> | undefined = route.state;
92
+ let current: NavigationState | PartialState<NavigationState> | undefined =
93
+ route.state;
93
94
  let config: ConfigItem | undefined = normalizedConfig?.screens?.[route.name];
94
95
  let params = { ...route.params } as NavigatorScreenParams<ParamListBase>;
95
96
 
@@ -1,13 +1,8 @@
1
- import type {
2
- InitialState,
3
- NavigationState,
4
- PartialState,
5
- } from '@react-navigation/routers';
1
+ import type { NavigationState, PartialState } from '@react-navigation/routers';
6
2
  import escape from 'escape-string-regexp';
7
3
  import queryString from 'query-string';
8
4
 
9
5
  import { arrayStartsWith } from './arrayStartsWith';
10
- import { findFocusedRoute } from './findFocusedRoute';
11
6
  import {
12
7
  combinePatternParts,
13
8
  getPatternParts,
@@ -58,13 +53,10 @@ type InitialRouteConfig = {
58
53
  parentScreens: string[];
59
54
  };
60
55
 
61
- type ResultState = PartialState<NavigationState> & {
62
- state?: ResultState | undefined;
63
- };
64
-
65
56
  type ParsedRoute = {
66
57
  name: string;
67
58
  params?: Record<string, unknown> | undefined;
59
+ path?: string | undefined;
68
60
  };
69
61
 
70
62
  type RoutePatternPart = PatternPart & { screen: string };
@@ -177,7 +169,7 @@ export function getStateFromPath<ParamList extends {}>(
177
169
  path: string,
178
170
  options?: Options<ParamList>,
179
171
  previous?: NavigationState
180
- ): ResultState | undefined {
172
+ ): PartialState<NavigationState> | undefined {
181
173
  const {
182
174
  initialRoutes,
183
175
  configs,
@@ -1016,7 +1008,7 @@ const createStateObject = (
1016
1008
  initialRoute: string | undefined,
1017
1009
  route: ParsedRoute,
1018
1010
  isEmpty: boolean
1019
- ): InitialState => {
1011
+ ): PartialState<NavigationState> => {
1020
1012
  if (isEmpty) {
1021
1013
  if (initialRoute) {
1022
1014
  return {
@@ -1047,7 +1039,32 @@ const createNestedStateObject = (
1047
1039
  routes: ParsedRoute[],
1048
1040
  initialRoutes: InitialRouteConfig[],
1049
1041
  routeConfig?: RouteConfig
1050
- ): InitialState | undefined => {
1042
+ ): PartialState<NavigationState> | undefined => {
1043
+ const focusedRoute = routes[routes.length - 1];
1044
+
1045
+ if (focusedRoute == null) {
1046
+ return undefined;
1047
+ }
1048
+
1049
+ focusedRoute.path = path.replace(/\/$/, '');
1050
+
1051
+ const queryParams = parseQueryParams(
1052
+ path,
1053
+ routeConfig?.parse,
1054
+ routeConfig?.pathParamNames,
1055
+ routeConfig?.explicitParamNames,
1056
+ routeConfig?.hasNestedScreens,
1057
+ focusedRoute.params
1058
+ );
1059
+
1060
+ if (!queryParams.valid) {
1061
+ return undefined;
1062
+ }
1063
+
1064
+ if (queryParams.params) {
1065
+ focusedRoute.params = { ...focusedRoute.params, ...queryParams.params };
1066
+ }
1067
+
1051
1068
  let route = routes.shift();
1052
1069
 
1053
1070
  if (route == null) {
@@ -1060,11 +1077,7 @@ const createNestedStateObject = (
1060
1077
 
1061
1078
  parentScreens.push(route.name);
1062
1079
 
1063
- const state: InitialState = createStateObject(
1064
- initialRoute,
1065
- route,
1066
- routes.length === 0
1067
- );
1080
+ const state = createStateObject(initialRoute, route, routes.length === 0);
1068
1081
 
1069
1082
  if (routes.length > 0) {
1070
1083
  let nestedState = state;
@@ -1104,31 +1117,6 @@ const createNestedStateObject = (
1104
1117
  }
1105
1118
  }
1106
1119
 
1107
- const focusedRoute = findFocusedRoute(state);
1108
-
1109
- if (focusedRoute == null) {
1110
- return undefined;
1111
- }
1112
-
1113
- focusedRoute.path = path.replace(/\/$/, '');
1114
-
1115
- const queryParams = parseQueryParams(
1116
- path,
1117
- routeConfig?.parse,
1118
- routeConfig?.pathParamNames,
1119
- routeConfig?.explicitParamNames,
1120
- routeConfig?.hasNestedScreens,
1121
- focusedRoute.params
1122
- );
1123
-
1124
- if (!queryParams.valid) {
1125
- return undefined;
1126
- }
1127
-
1128
- if (queryParams.params) {
1129
- focusedRoute.params = { ...focusedRoute.params, ...queryParams.params };
1130
- }
1131
-
1132
1120
  return state;
1133
1121
  };
1134
1122
 
@@ -44,7 +44,6 @@ export function getStateFromRouteParams(
44
44
  'path' in params && typeof params.path === 'string'
45
45
  ? params.path
46
46
  : undefined,
47
- // @ts-expect-error this is fine 🔥
48
47
  state:
49
48
  'params' in params &&
50
49
  typeof params.params === 'object' &&
package/src/index.tsx CHANGED
@@ -19,6 +19,7 @@ export { NavigationHelpersContext } from './NavigationHelpersContext';
19
19
  export { NavigationIndependentTree } from './NavigationIndependentTree';
20
20
  export { NavigationMetaContext } from './NavigationMetaContext';
21
21
  export {
22
+ IsScreenContext,
22
23
  NavigationContext,
23
24
  NavigationProvider,
24
25
  NavigationRouteContext,
@@ -0,0 +1,2 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
2
+ export interface Theme {}
package/src/types.tsx CHANGED
@@ -10,6 +10,7 @@ import type {
10
10
  } from '@react-navigation/routers';
11
11
  import type * as React from 'react';
12
12
 
13
+ import type { Theme as InternalTheme } from './internal';
13
14
  import type {
14
15
  FlatType,
15
16
  KeyOf,
@@ -51,8 +52,7 @@ export interface RootNavigator {}
51
52
  * }
52
53
  * ```
53
54
  */
54
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
55
- export interface Theme {}
55
+ export interface Theme extends InternalTheme {}
56
56
 
57
57
  export type RootParamList =
58
58
  RootNavigator extends PrivateValueStore<
@@ -1360,7 +1360,7 @@ type NavigationListForGroups<ParentList, Groups> = Groups extends {}
1360
1360
 
1361
1361
  export type NavigationContainerRef<ParamList extends {}> = Omit<
1362
1362
  NavigationHelpers<ParamList>,
1363
- keyof NavigationHelpersRoute<{}>
1363
+ keyof NavigationHelpersRoute<{}> | 'isFocused'
1364
1364
  > &
1365
1365
  NavigationHelpersRoute<{}> &
1366
1366
  EventConsumer<NavigationContainerEventMap> & {
@@ -1386,10 +1386,6 @@ export type NavigationContainerRef<ParamList extends {}> = Omit<
1386
1386
  * Whether the navigation container is ready to handle actions.
1387
1387
  */
1388
1388
  isReady(): boolean;
1389
- /**
1390
- * Stub function for setOptions on navigation object for use with useNavigation.
1391
- */
1392
- setOptions(): never;
1393
1389
  };
1394
1390
 
1395
1391
  export type NavigationContainerRefWithCurrent<ParamList extends {}> =
@@ -78,6 +78,7 @@ type Options<
78
78
  onAction: (action: NavigationAction) => boolean;
79
79
  getState: () => State;
80
80
  setState: (state: State) => void;
81
+ subscribe: (callback: () => void) => () => void;
81
82
  addListener: AddListener;
82
83
  addKeyedListener: AddKeyedListener;
83
84
  onRouteFocus: (key: string) => void;
@@ -107,6 +108,7 @@ export function useDescriptors<
107
108
  onAction,
108
109
  getState,
109
110
  setState,
111
+ subscribe,
110
112
  addListener,
111
113
  addKeyedListener,
112
114
  onRouteFocus,
@@ -123,8 +125,8 @@ export function useDescriptors<
123
125
  onOptionsChange,
124
126
  scheduleUpdate,
125
127
  flushUpdates,
126
- stackRef,
127
128
  getIsStateEmitted,
129
+ withStackTrace,
128
130
  } = React.use(NavigationBuilderContext);
129
131
 
130
132
  const context = React.useMemo(
@@ -140,7 +142,7 @@ export function useDescriptors<
140
142
  getIsStateEmitted,
141
143
  scheduleUpdate,
142
144
  flushUpdates,
143
- stackRef,
145
+ withStackTrace,
144
146
  }),
145
147
  [
146
148
  navigation,
@@ -154,7 +156,7 @@ export function useDescriptors<
154
156
  getIsStateEmitted,
155
157
  scheduleUpdate,
156
158
  flushUpdates,
157
- stackRef,
159
+ withStackTrace,
158
160
  ]
159
161
  );
160
162
 
@@ -257,6 +259,7 @@ export function useDescriptors<
257
259
  routeState={routeState}
258
260
  getState={getState}
259
261
  setState={setState}
262
+ subscribe={subscribe}
260
263
  options={customOptions}
261
264
  clearOptions={clearOptions}
262
265
  />
@@ -14,6 +14,7 @@ export function useKeyedChildListeners() {
14
14
  }>(
15
15
  Object.assign(Object.create(null), {
16
16
  getState: {},
17
+ getNavigation: {},
17
18
  beforeRemove: {},
18
19
  })
19
20
  );
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
 
3
- import { NavigationContainerRefContext } from './NavigationContainerRefContext';
4
3
  import { NavigationContext } from './NavigationProvider';
4
+ import { NavigationRootContext } from './NavigationRootContext';
5
5
  import type {
6
6
  GenericNavigation,
7
7
  NavigationListForNavigator,
@@ -30,7 +30,7 @@ export function useNavigation<
30
30
  keyof NavigationListForNested<Navigator>,
31
31
  >(name: RouteName): NavigationListForNested<Navigator>[RouteName];
32
32
  export function useNavigation(name?: string): unknown {
33
- const root = React.use(NavigationContainerRefContext);
33
+ const root = React.use(NavigationRootContext);
34
34
  const navigation = React.use(NavigationContext);
35
35
 
36
36
  if (name === undefined) {
@@ -22,7 +22,7 @@ import { isArrayEqual } from './isArrayEqual';
22
22
  import { NavigationBuilderContext } from './NavigationBuilderContext';
23
23
  import { NavigationHelpersContext } from './NavigationHelpersContext';
24
24
  import { NavigationMetaContext } from './NavigationMetaContext';
25
- import { NavigationRouteContext } from './NavigationProvider';
25
+ import { IsScreenContext, NavigationRouteContext } from './NavigationProvider';
26
26
  import { NavigationStateContext } from './NavigationStateContext';
27
27
  import { PreventRemoveProvider } from './PreventRemoveProvider';
28
28
  import { Screen } from './Screen';
@@ -47,6 +47,7 @@ import { useLazyValue } from './useLazyValue';
47
47
  import { useNavigationHelpers } from './useNavigationHelpers';
48
48
  import { NavigationStateListenerProvider } from './useNavigationState';
49
49
  import { useOnAction } from './useOnAction';
50
+ import { useOnGetNavigation } from './useOnGetNavigation';
50
51
  import { useOnGetState } from './useOnGetState';
51
52
  import { useOnRouteFocus } from './useOnRouteFocus';
52
53
  import { useRegisterNavigator } from './useRegisterNavigator';
@@ -453,6 +454,7 @@ export function useNavigationBuilder<
453
454
  state: currentState,
454
455
  getState: getCurrentState,
455
456
  setState: setCurrentState,
457
+ subscribe,
456
458
  setKey,
457
459
  getKey,
458
460
  getIsInitial,
@@ -1004,7 +1006,6 @@ export function useNavigationBuilder<
1004
1006
  onAction,
1005
1007
  onUnhandledAction,
1006
1008
  getState,
1007
- state,
1008
1009
  emitter,
1009
1010
  router,
1010
1011
  });
@@ -1019,6 +1020,8 @@ export function useNavigationBuilder<
1019
1020
  getStateListeners: keyedListeners.getState,
1020
1021
  });
1021
1022
 
1023
+ useOnGetNavigation({ navigation });
1024
+
1022
1025
  const descriptors = useDescriptors<
1023
1026
  State,
1024
1027
  ActionHelpers,
@@ -1033,6 +1036,7 @@ export function useNavigationBuilder<
1033
1036
  onAction,
1034
1037
  getState,
1035
1038
  setState,
1039
+ subscribe,
1036
1040
  onRouteFocus,
1037
1041
  addListener,
1038
1042
  addKeyedListener,
@@ -1062,11 +1066,17 @@ export function useNavigationBuilder<
1062
1066
  <NavigationMetaContext.Provider value={undefined}>
1063
1067
  <NavigationHelpersContext.Provider value={navigation}>
1064
1068
  <NavigationStateListenerProvider
1069
+ isSynced={!shouldUpdate}
1065
1070
  state={state}
1066
- getState={navigation.getState}
1071
+ getState={getState}
1072
+ subscribe={subscribe}
1067
1073
  >
1068
1074
  <FocusedRouteKeyContext.Provider value={focusedRoute.key}>
1069
- <PreventRemoveProvider>{element}</PreventRemoveProvider>
1075
+ <PreventRemoveProvider>
1076
+ <IsScreenContext.Provider value={false}>
1077
+ {element}
1078
+ </IsScreenContext.Provider>
1079
+ </PreventRemoveProvider>
1070
1080
  </FocusedRouteKeyContext.Provider>
1071
1081
  </NavigationStateListenerProvider>
1072
1082
  </NavigationHelpersContext.Provider>
@@ -72,8 +72,9 @@ export function useNavigationCache<
72
72
  router,
73
73
  emitter,
74
74
  }: Options<State, ScreenOptions, EventMap>) {
75
- const parentNavigationHelpers = React.use(NavigationContext);
76
- const { stackRef } = React.use(NavigationBuilderContext);
75
+ const parentNavigation = React.use(NavigationContext);
76
+
77
+ const { withStackTrace } = React.use(NavigationBuilderContext);
77
78
 
78
79
  // Cache object which holds navigation objects for each screen
79
80
  // We use `React.useMemo` instead of `React.useRef` coz we want to invalidate it when deps change
@@ -88,7 +89,7 @@ export function useNavigationCache<
88
89
  >,
89
90
  }),
90
91
  // eslint-disable-next-line react-hooks/exhaustive-deps
91
- [getState, parentNavigationHelpers, navigation, setOptions, emitter]
92
+ [getState, parentNavigation, navigation, setOptions, emitter]
92
93
  );
93
94
 
94
95
  const next = routes.reduce<
@@ -100,34 +101,12 @@ export function useNavigationCache<
100
101
  // If a cached navigation object already exists, reuse it
101
102
  acc[route.key] = previous;
102
103
  } else {
103
- const withStack = (callback: () => void) => {
104
- let isStackSet = false;
105
-
106
- try {
107
- if (
108
- process.env.NODE_ENV !== 'production' &&
109
- stackRef &&
110
- !stackRef.current
111
- ) {
112
- // Capture the stack trace for devtools
113
- stackRef.current = new Error().stack;
114
- isStackSet = true;
115
- }
116
-
117
- callback();
118
- } finally {
119
- if (isStackSet && stackRef) {
120
- stackRef.current = undefined;
121
- }
122
- }
123
- };
124
-
125
104
  const dispatch = (
126
105
  thunk:
127
106
  | NavigationAction
128
107
  | ((state: State) => NavigationAction | null | undefined)
129
108
  ) => {
130
- withStack(() =>
109
+ withStackTrace(dispatch, () => {
131
110
  React.startTransition(() => {
132
111
  const action =
133
112
  typeof thunk === 'function' ? thunk(getState()) : thunk;
@@ -135,8 +114,8 @@ export function useNavigationCache<
135
114
  if (action != null) {
136
115
  navigation.dispatch({ source: route.key, ...action });
137
116
  }
138
- })
139
- );
117
+ });
118
+ });
140
119
  };
141
120
 
142
121
  const actions = {
@@ -146,9 +125,13 @@ export function useNavigationCache<
146
125
 
147
126
  const helpers = Object.keys(actions).reduce<Record<string, () => void>>(
148
127
  (acc, name) => {
149
- acc[name] = (...args: any) =>
150
- // @ts-expect-error: name is a valid key, but TypeScript is dumb
151
- dispatch(actions[name](...args));
128
+ const helper = (...args: any) =>
129
+ withStackTrace(helper, () =>
130
+ // @ts-expect-error: name is a valid key, but TypeScript is dumb
131
+ dispatch(actions[name](...args))
132
+ );
133
+
134
+ acc[name] = helper;
152
135
 
153
136
  return acc;
154
137
  },
@@ -172,10 +155,18 @@ export function useNavigationCache<
172
155
  }
173
156
 
174
157
  if (routeName !== undefined) {
175
- return parentNavigationHelpers?.getParent(routeName);
158
+ const parent = parentNavigation?.getParent(routeName);
159
+
160
+ if (parent === undefined) {
161
+ throw new Error(
162
+ `Couldn't find a navigation object for '${routeName}' in current or any parent screens. Is your component inside the correct screen?`
163
+ );
164
+ }
165
+
166
+ return parent;
176
167
  }
177
168
 
178
- return parentNavigationHelpers;
169
+ return parentNavigation;
179
170
  },
180
171
  setOptions: (options: Partial<ScreenOptions>) => {
181
172
  setOptions((o) => ({