@react-navigation/core 8.0.0-alpha.24 → 8.0.0-alpha.26

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 (136) hide show
  1. package/lib/module/BaseNavigationContainer.js +5 -2
  2. package/lib/module/BaseNavigationContainer.js.map +1 -1
  3. package/lib/module/DataLoading.js +102 -0
  4. package/lib/module/DataLoading.js.map +1 -0
  5. package/lib/module/NavigationIndependentTree.js +7 -6
  6. package/lib/module/NavigationIndependentTree.js.map +1 -1
  7. package/lib/module/NavigationProvider.js +12 -12
  8. package/lib/module/NavigationProvider.js.map +1 -1
  9. package/lib/module/PreventRemoveContext.js.map +1 -1
  10. package/lib/module/PreventRemoveProvider.js +34 -24
  11. package/lib/module/PreventRemoveProvider.js.map +1 -1
  12. package/lib/module/StaticNavigation.js +14 -6
  13. package/lib/module/StaticNavigation.js.map +1 -1
  14. package/lib/module/StaticTreeContext.js +5 -0
  15. package/lib/module/StaticTreeContext.js.map +1 -0
  16. package/lib/module/checkSerializable.js +7 -9
  17. package/lib/module/checkSerializable.js.map +1 -1
  18. package/lib/module/createNavigationContainerRef.js +6 -8
  19. package/lib/module/createNavigationContainerRef.js.map +1 -1
  20. package/lib/module/createNavigatorFactory.js.map +1 -1
  21. package/lib/module/getActionFromState.js +11 -11
  22. package/lib/module/getActionFromState.js.map +1 -1
  23. package/lib/module/getPathFromState.js +38 -39
  24. package/lib/module/getPathFromState.js.map +1 -1
  25. package/lib/module/getPatternParts.js +2 -2
  26. package/lib/module/getPatternParts.js.map +1 -1
  27. package/lib/module/getStateFromPath.js +72 -45
  28. package/lib/module/getStateFromPath.js.map +1 -1
  29. package/lib/module/index.js +1 -0
  30. package/lib/module/index.js.map +1 -1
  31. package/lib/module/types.js.map +1 -1
  32. package/lib/module/useEventEmitter.js +1 -1
  33. package/lib/module/useEventEmitter.js.map +1 -1
  34. package/lib/module/useKeyedChildListeners.js +4 -2
  35. package/lib/module/useKeyedChildListeners.js.map +1 -1
  36. package/lib/module/useNavigation.js +2 -15
  37. package/lib/module/useNavigation.js.map +1 -1
  38. package/lib/module/useNavigationBuilder.js +4 -14
  39. package/lib/module/useNavigationBuilder.js.map +1 -1
  40. package/lib/module/useNavigationCache.js +19 -14
  41. package/lib/module/useNavigationCache.js.map +1 -1
  42. package/lib/module/useNavigationHelpers.js +27 -9
  43. package/lib/module/useNavigationHelpers.js.map +1 -1
  44. package/lib/module/useNavigationState.js +69 -36
  45. package/lib/module/useNavigationState.js.map +1 -1
  46. package/lib/module/useOnAction.js +21 -2
  47. package/lib/module/useOnAction.js.map +1 -1
  48. package/lib/module/useOptionsGetters.js +3 -1
  49. package/lib/module/useOptionsGetters.js.map +1 -1
  50. package/lib/module/usePreventRemove.js +9 -2
  51. package/lib/module/usePreventRemove.js.map +1 -1
  52. package/lib/module/useRouteCache.js +14 -7
  53. package/lib/module/useRouteCache.js.map +1 -1
  54. package/lib/module/useScheduleUpdate.js +8 -8
  55. package/lib/module/useScheduleUpdate.js.map +1 -1
  56. package/lib/module/useSyncState.js +4 -1
  57. package/lib/module/useSyncState.js.map +1 -1
  58. package/lib/module/validatePathConfig.js.map +1 -1
  59. package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
  60. package/lib/typescript/src/DataLoading.d.ts +20 -0
  61. package/lib/typescript/src/DataLoading.d.ts.map +1 -0
  62. package/lib/typescript/src/NavigationProvider.d.ts +0 -1
  63. package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
  64. package/lib/typescript/src/PreventRemoveContext.d.ts +1 -0
  65. package/lib/typescript/src/PreventRemoveContext.d.ts.map +1 -1
  66. package/lib/typescript/src/PreventRemoveProvider.d.ts.map +1 -1
  67. package/lib/typescript/src/StaticNavigation.d.ts +65 -39
  68. package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
  69. package/lib/typescript/src/StaticTreeContext.d.ts +4 -0
  70. package/lib/typescript/src/StaticTreeContext.d.ts.map +1 -0
  71. package/lib/typescript/src/checkSerializable.d.ts +4 -2
  72. package/lib/typescript/src/checkSerializable.d.ts.map +1 -1
  73. package/lib/typescript/src/createNavigationContainerRef.d.ts.map +1 -1
  74. package/lib/typescript/src/createNavigatorFactory.d.ts +9 -5
  75. package/lib/typescript/src/createNavigatorFactory.d.ts.map +1 -1
  76. package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
  77. package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
  78. package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
  79. package/lib/typescript/src/index.d.ts +2 -1
  80. package/lib/typescript/src/index.d.ts.map +1 -1
  81. package/lib/typescript/src/types.d.ts +27 -18
  82. package/lib/typescript/src/types.d.ts.map +1 -1
  83. package/lib/typescript/src/useKeyedChildListeners.d.ts.map +1 -1
  84. package/lib/typescript/src/useNavigation.d.ts.map +1 -1
  85. package/lib/typescript/src/useNavigationBuilder.d.ts +1 -1
  86. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  87. package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
  88. package/lib/typescript/src/useNavigationHelpers.d.ts +3 -4
  89. package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
  90. package/lib/typescript/src/useNavigationState.d.ts +11 -1
  91. package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
  92. package/lib/typescript/src/useOnAction.d.ts.map +1 -1
  93. package/lib/typescript/src/useOptionsGetters.d.ts.map +1 -1
  94. package/lib/typescript/src/usePreventRemove.d.ts.map +1 -1
  95. package/lib/typescript/src/usePreventRemoveContext.d.ts +1 -0
  96. package/lib/typescript/src/usePreventRemoveContext.d.ts.map +1 -1
  97. package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
  98. package/lib/typescript/src/useScheduleUpdate.d.ts +5 -4
  99. package/lib/typescript/src/useScheduleUpdate.d.ts.map +1 -1
  100. package/lib/typescript/src/useSyncState.d.ts.map +1 -1
  101. package/lib/typescript/src/utilities.d.ts +17 -0
  102. package/lib/typescript/src/utilities.d.ts.map +1 -1
  103. package/lib/typescript/src/validatePathConfig.d.ts.map +1 -1
  104. package/package.json +3 -5
  105. package/src/BaseNavigationContainer.tsx +9 -2
  106. package/src/DataLoading.tsx +139 -0
  107. package/src/NavigationIndependentTree.tsx +11 -11
  108. package/src/NavigationProvider.tsx +13 -16
  109. package/src/PreventRemoveContext.tsx +1 -0
  110. package/src/PreventRemoveProvider.tsx +48 -43
  111. package/src/StaticNavigation.tsx +157 -93
  112. package/src/StaticTreeContext.tsx +7 -0
  113. package/src/checkSerializable.tsx +20 -14
  114. package/src/createNavigationContainerRef.tsx +12 -9
  115. package/src/createNavigatorFactory.tsx +16 -28
  116. package/src/getActionFromState.tsx +18 -16
  117. package/src/getPathFromState.tsx +69 -65
  118. package/src/getPatternParts.tsx +2 -2
  119. package/src/getStateFromPath.tsx +107 -66
  120. package/src/index.tsx +2 -0
  121. package/src/types.tsx +74 -55
  122. package/src/useEventEmitter.tsx +1 -1
  123. package/src/useKeyedChildListeners.tsx +4 -2
  124. package/src/useNavigation.tsx +2 -21
  125. package/src/useNavigationBuilder.tsx +6 -16
  126. package/src/useNavigationCache.tsx +26 -19
  127. package/src/useNavigationHelpers.tsx +26 -9
  128. package/src/useNavigationState.tsx +90 -60
  129. package/src/useOnAction.tsx +49 -1
  130. package/src/useOptionsGetters.tsx +4 -1
  131. package/src/usePreventRemove.tsx +11 -2
  132. package/src/useRouteCache.tsx +19 -11
  133. package/src/useScheduleUpdate.tsx +8 -7
  134. package/src/useSyncState.tsx +5 -1
  135. package/src/utilities.tsx +28 -0
  136. package/src/validatePathConfig.tsx +1 -0
@@ -5,9 +5,9 @@ import {
5
5
  NamedRouteContextListContext,
6
6
  NavigationContext,
7
7
  NavigationRouteContext,
8
- NavigationRouteNameContext,
9
8
  } from './NavigationProvider';
10
9
  import { IsFocusedContext } from './useIsFocused';
10
+ import { NamedNavigationStateListenerListContext } from './useNavigationState';
11
11
 
12
12
  /**
13
13
  * Component to make the child navigation container independent of parent containers.
@@ -19,18 +19,18 @@ export function NavigationIndependentTree({
19
19
  }) {
20
20
  return (
21
21
  // We need to clear any existing contexts for nested independent container to work correctly
22
- <NamedRouteContextListContext.Provider value={undefined}>
23
- <NavigationRouteContext.Provider value={undefined}>
24
- <NavigationContext.Provider value={undefined}>
25
- <IsFocusedContext.Provider value={undefined}>
26
- <NavigationRouteNameContext.Provider value={undefined}>
22
+ <NamedNavigationStateListenerListContext.Provider value={undefined}>
23
+ <NamedRouteContextListContext.Provider value={undefined}>
24
+ <NavigationRouteContext.Provider value={undefined}>
25
+ <NavigationContext.Provider value={undefined}>
26
+ <IsFocusedContext.Provider value={undefined}>
27
27
  <NavigationIndependentTreeContext.Provider value={true}>
28
28
  {children}
29
29
  </NavigationIndependentTreeContext.Provider>
30
- </NavigationRouteNameContext.Provider>
31
- </IsFocusedContext.Provider>
32
- </NavigationContext.Provider>
33
- </NavigationRouteContext.Provider>
34
- </NamedRouteContextListContext.Provider>
30
+ </IsFocusedContext.Provider>
31
+ </NavigationContext.Provider>
32
+ </NavigationRouteContext.Provider>
33
+ </NamedRouteContextListContext.Provider>
34
+ </NamedNavigationStateListenerListContext.Provider>
35
35
  );
36
36
  }
@@ -4,6 +4,7 @@ import * as React from 'react';
4
4
  import type { NavigationProp } from './types';
5
5
  import { FocusedRouteKeyContext, IsFocusedContext } from './useIsFocused';
6
6
  import { useLazyValue } from './useLazyValue';
7
+ import { NamedNavigationStateListenerProvider } from './useNavigationState';
7
8
 
8
9
  /**
9
10
  * Context which holds the route prop for a screen.
@@ -19,10 +20,6 @@ export const NavigationContext = React.createContext<
19
20
  NavigationProp<ParamListBase> | undefined
20
21
  >(undefined);
21
22
 
22
- export const NavigationRouteNameContext = React.createContext<
23
- string | undefined
24
- >(undefined);
25
-
26
23
  type Props = {
27
24
  route: Route<string>;
28
25
  navigation: NavigationProp<ParamListBase>;
@@ -61,18 +58,18 @@ export function NavigationProvider({ route, navigation, children }: Props) {
61
58
  : false;
62
59
 
63
60
  return (
64
- <NamedRouteContextListContext.Provider value={NamedRouteContextList}>
65
- <NamedRouteContext.Provider value={route}>
66
- <NavigationRouteContext.Provider value={route}>
67
- <NavigationContext.Provider value={navigation}>
68
- <IsFocusedContext.Provider value={isFocused}>
69
- <NavigationRouteNameContext.Provider value={route.name}>
61
+ <NamedNavigationStateListenerProvider name={route.name}>
62
+ <NamedRouteContextListContext.Provider value={NamedRouteContextList}>
63
+ <NamedRouteContext.Provider value={route}>
64
+ <NavigationRouteContext.Provider value={route}>
65
+ <NavigationContext.Provider value={navigation}>
66
+ <IsFocusedContext.Provider value={isFocused}>
70
67
  {children}
71
- </NavigationRouteNameContext.Provider>
72
- </IsFocusedContext.Provider>
73
- </NavigationContext.Provider>
74
- </NavigationRouteContext.Provider>
75
- </NamedRouteContext.Provider>
76
- </NamedRouteContextListContext.Provider>
68
+ </IsFocusedContext.Provider>
69
+ </NavigationContext.Provider>
70
+ </NavigationRouteContext.Provider>
71
+ </NamedRouteContext.Provider>
72
+ </NamedRouteContextListContext.Provider>
73
+ </NamedNavigationStateListenerProvider>
77
74
  );
78
75
  }
@@ -14,6 +14,7 @@ export const PreventRemoveContext = React.createContext<
14
14
  routeKey: string,
15
15
  preventRemove: boolean
16
16
  ) => void;
17
+ notifyPreventRemove: () => void;
17
18
  }
18
19
  | undefined
19
20
  >(undefined);
@@ -13,13 +13,7 @@ type Props = {
13
13
  children: React.ReactNode;
14
14
  };
15
15
 
16
- type PreventedRoutesMap = Map<
17
- string,
18
- {
19
- routeKey: string;
20
- preventRemove: boolean;
21
- }
22
- >;
16
+ type PreventedRoutesMap = Map<string, string>;
23
17
 
24
18
  /**
25
19
  * Util function to transform map of prevented routes to a simpler object.
@@ -27,17 +21,11 @@ type PreventedRoutesMap = Map<
27
21
  const transformPreventedRoutes = (
28
22
  preventedRoutesMap: PreventedRoutesMap
29
23
  ): PreventedRoutes => {
30
- const preventedRoutesToTransform = [...preventedRoutesMap.values()];
24
+ const preventedRoutes: PreventedRoutes = {};
31
25
 
32
- const preventedRoutes = preventedRoutesToTransform.reduce<PreventedRoutes>(
33
- (acc, { routeKey, preventRemove }) => {
34
- acc[routeKey] = {
35
- preventRemove: acc[routeKey]?.preventRemove || preventRemove,
36
- };
37
- return acc;
38
- },
39
- {}
40
- );
26
+ for (const routeKey of preventedRoutesMap.values()) {
27
+ preventedRoutes[routeKey] = { preventRemove: true };
28
+ }
41
29
 
42
30
  return preventedRoutes;
43
31
  };
@@ -50,12 +38,15 @@ export function PreventRemoveProvider({ children }: Props) {
50
38
  const [preventedRoutesMap, setPreventedRoutesMap] =
51
39
  React.useState<PreventedRoutesMap>(() => new Map());
52
40
 
41
+ const registry = React.useRef<PreventedRoutesMap>(new Map());
42
+
53
43
  const navigation = React.use(NavigationHelpersContext);
54
44
  const route = React.use(NavigationRouteContext);
55
45
 
56
46
  const preventRemoveContextValue = React.use(PreventRemoveContext);
57
47
  // take `setPreventRemove` from parent context - if exist it means we're in a nested context
58
48
  const setParentPrevented = preventRemoveContextValue?.setPreventRemove;
49
+ const notifyParentPrevented = preventRemoveContextValue?.notifyPreventRemove;
59
50
 
60
51
  const setPreventRemove = useLatestCallback(
61
52
  (id: string, routeKey: string, preventRemove: boolean): void => {
@@ -71,36 +62,49 @@ export function PreventRemoveProvider({ children }: Props) {
71
62
  );
72
63
  }
73
64
 
74
- setPreventedRoutesMap((prevPrevented) => {
75
- // values haven't changed - do nothing
76
- if (
77
- routeKey === prevPrevented.get(id)?.routeKey &&
78
- preventRemove === prevPrevented.get(id)?.preventRemove
79
- ) {
80
- return prevPrevented;
81
- }
82
-
83
- const nextPrevented = new Map(prevPrevented);
84
-
85
- if (preventRemove) {
86
- nextPrevented.set(id, {
87
- routeKey,
88
- preventRemove,
89
- });
90
- } else {
91
- nextPrevented.delete(id);
92
- }
93
-
94
- return nextPrevented;
95
- });
65
+ if (preventRemove) {
66
+ registry.current.set(id, routeKey);
67
+ } else {
68
+ registry.current.delete(id);
69
+ }
96
70
  }
97
71
  );
98
72
 
99
- const isPrevented = [...preventedRoutesMap.values()].some(
100
- ({ preventRemove }) => preventRemove
101
- );
73
+ const notifyPreventRemove = useLatestCallback(() => {
74
+ // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
75
+ setPreventedRoutesMap((prevPrevented) => {
76
+ const nextPrevented = registry.current;
102
77
 
78
+ const hasChanged =
79
+ prevPrevented.size !== nextPrevented.size ||
80
+ [...nextPrevented].some(
81
+ ([id, routeKey]) => prevPrevented.get(id) !== routeKey
82
+ );
83
+
84
+ // values haven't changed - do nothing
85
+ if (!hasChanged) {
86
+ return prevPrevented;
87
+ }
88
+
89
+ return new Map(nextPrevented);
90
+ });
91
+ });
92
+
93
+ // Sync the state on every commit in case the registry was changed by a screen
94
+ // unmounting while hidden with `<Activity>`, where no passive effects run
95
+ // Also notify the parent so nested prevention propagates upwards
103
96
  React.useEffect(() => {
97
+ notifyPreventRemove();
98
+ notifyParentPrevented?.();
99
+
100
+ return () => {
101
+ notifyParentPrevented?.();
102
+ };
103
+ });
104
+
105
+ const isPrevented = preventedRoutesMap.size > 0;
106
+
107
+ React.useInsertionEffect(() => {
104
108
  if (route?.key !== undefined && setParentPrevented !== undefined) {
105
109
  // when route is defined (and setParentPrevented) it means we're in a nested stack
106
110
  // route.key then will be the route key of parent
@@ -116,9 +120,10 @@ export function PreventRemoveProvider({ children }: Props) {
116
120
  const value = React.useMemo(
117
121
  () => ({
118
122
  setPreventRemove,
123
+ notifyPreventRemove,
119
124
  preventedRoutes: transformPreventedRoutes(preventedRoutesMap),
120
125
  }),
121
- [setPreventRemove, preventedRoutesMap]
126
+ [setPreventRemove, notifyPreventRemove, preventedRoutesMap]
122
127
  );
123
128
 
124
129
  return (
@@ -1,4 +1,8 @@
1
- import type { NavigationState, ParamListBase } from '@react-navigation/routers';
1
+ import type {
2
+ NavigationState,
3
+ ParamListBase,
4
+ Route,
5
+ } from '@react-navigation/routers';
2
6
  import * as React from 'react';
3
7
  import { isValidElementType } from 'react-is';
4
8
 
@@ -7,10 +11,11 @@ import {
7
11
  getPatternParts,
8
12
  type PatternPart,
9
13
  } from './getPatternParts';
14
+ import { StaticTreeContext } from './StaticTreeContext';
10
15
  import type {
11
- DefaultNavigatorOptions,
12
16
  EventMapBase,
13
17
  NavigationListBase,
18
+ NavigatorProps,
14
19
  NavigatorScreenParams,
15
20
  NavigatorTypeBagBase,
16
21
  NavigatorTypeBagFor,
@@ -27,6 +32,8 @@ import type {
27
32
  HasArguments,
28
33
  InferParamsFromLinking,
29
34
  KeysOf,
35
+ NoExcessObject,
36
+ NotUndefinedObject,
30
37
  StandardSchemaV1,
31
38
  UnionToIntersection,
32
39
  ValidPathPattern,
@@ -105,7 +112,13 @@ type ShouldInferFromLinking<Linking> = Linking extends {
105
112
  type ParamsForConfig<Linking, Screen, Params = ParamsForScreen<Screen>> =
106
113
  ShouldInferFromLinking<Linking> extends true
107
114
  ? [Screen] extends [{ config: any }]
108
- ? FlatType<InferParamsFromLinking<Linking>> & Params
115
+ ? undefined extends Params
116
+ ? FlatType<InferParamsFromLinking<Linking>> & Params
117
+ : // Optional linking params make the whole route params type optional when intersected.
118
+ // e.g. `({...} | undefined) & NavigatorScreenParams<{...}>` adds `| undefined` to the whole type.
119
+ // But if nested navigator had required params, it should still be required.
120
+ // So we remove `| undefined` from linking params before intersecting with nested navigator params.
121
+ FlatType<NotUndefinedObject<InferParamsFromLinking<Linking>>> & Params
109
122
  : undefined extends Params
110
123
  ? FlatType<InferParamsFromLinking<Linking>>
111
124
  : FlatType<InferParamsFromLinking<Linking> & Params>
@@ -125,15 +138,7 @@ type ParamListForScreens<Screens> = {
125
138
  : ParamsForScreenEntry<Screens[Key]>;
126
139
  };
127
140
 
128
- type ParamListForGroups<
129
- Groups extends
130
- | Readonly<{
131
- [key: string]: {
132
- screens: {};
133
- };
134
- }>
135
- | undefined,
136
- > = Groups extends {
141
+ type ParamListForGroups<Groups> = Groups extends {
137
142
  [key: string]: {
138
143
  screens: infer Screens;
139
144
  };
@@ -141,27 +146,20 @@ type ParamListForGroups<
141
146
  ? ParamListForScreens<UnionToIntersection<Screens>>
142
147
  : {};
143
148
 
144
- type RouteTypeBase = {
145
- key: string;
146
- name: string;
147
- path?: string | undefined;
148
- history?: { type: 'params'; params: object }[] | undefined;
149
- };
150
-
151
- type RouteType<Params, P = AnyToUnknown<Params>> = Readonly<
152
- RouteTypeBase &
149
+ type StaticRoute<Params, P = AnyToUnknown<Params>> = Readonly<
150
+ Omit<Route<string>, 'params'> &
153
151
  (undefined extends Params
154
152
  ? {
155
153
  /**
156
154
  * Params for this route
157
155
  */
158
- params?: P;
156
+ params?: P extends object ? Readonly<P> : P;
159
157
  }
160
158
  : {
161
159
  /**
162
160
  * Params for this route
163
161
  */
164
- params: P;
162
+ params: Readonly<P>;
165
163
  })
166
164
  >;
167
165
 
@@ -222,23 +220,25 @@ type StaticScreenConfigLinkingAlias = {
222
220
  shared?: boolean;
223
221
  };
224
222
 
223
+ type StaticScreenConfigLinkingExtras = {
224
+ /**
225
+ * Additional path alias that will be matched to the same screen.
226
+ */
227
+ alias?: (string | StaticScreenConfigLinkingAlias)[];
228
+ /**
229
+ * Name of the initial route to use for the nested navigator when the path matches.
230
+ */
231
+ initialRouteName?: string | undefined;
232
+ /**
233
+ * Path configuration for child screens in the nested navigator.
234
+ * Only supported when the screen renders a navigator with dynamic API.
235
+ */
236
+ screens?: PathConfigMap<ParamListBase> | undefined;
237
+ };
238
+
225
239
  export type StaticScreenConfigLinking =
226
240
  | string
227
- | (StaticScreenConfigLinkingAlias & {
228
- /**
229
- * Additional path alias that will be matched to the same screen.
230
- */
231
- alias?: (string | StaticScreenConfigLinkingAlias)[];
232
- /**
233
- * Name of the initial route to use for the nested navigator when the path matches.
234
- */
235
- initialRouteName?: string | undefined;
236
- /**
237
- * Path configuration for child screens in the nested navigator.
238
- * Only supported when the screen renders a navigator with dynamic API.
239
- */
240
- screens?: PathConfigMap<ParamListBase> | undefined;
241
- })
241
+ | (StaticScreenConfigLinkingAlias & StaticScreenConfigLinkingExtras)
242
242
  | null
243
243
  | undefined;
244
244
 
@@ -288,7 +288,7 @@ export type StaticScreenConfig<
288
288
  options?:
289
289
  | ScreenOptions
290
290
  | ((props: {
291
- route: RouteType<Params>;
291
+ route: StaticRoute<Params>;
292
292
  navigation: Navigation;
293
293
  theme: Theme;
294
294
  }) => ScreenOptions);
@@ -308,7 +308,7 @@ export type StaticScreenConfig<
308
308
  listeners?:
309
309
  | ScreenListeners<State, EventMap>
310
310
  | ((props: {
311
- route: RouteType<Params>;
311
+ route: StaticRoute<Params>;
312
312
  navigation: Navigation;
313
313
  }) => ScreenListeners<State, EventMap>);
314
314
 
@@ -328,7 +328,7 @@ export type StaticScreenConfig<
328
328
  *
329
329
  */
330
330
  layout?: (props: {
331
- route: RouteType<Params>;
331
+ route: StaticRoute<Params>;
332
332
  options: ScreenOptions;
333
333
  navigation: Navigation;
334
334
  theme: Theme;
@@ -355,7 +355,9 @@ export type StaticScreenConfig<
355
355
  * getId: ({ params }) => params?.userId,
356
356
  * ```
357
357
  */
358
- getId?: (props: { params: AnyToUnknown<Params> }) => string | undefined;
358
+ getId?: (props: {
359
+ params: AnyToUnknown<Params extends object ? Readonly<Params> : Params>;
360
+ }) => string | undefined;
359
361
 
360
362
  /**
361
363
  * Linking config for the screen.
@@ -377,11 +379,27 @@ export type StaticScreenConfig<
377
379
  * Optional key for this screen.
378
380
  */
379
381
  navigationKey?: string;
382
+
383
+ /**
384
+ * Loader function to start loading data when the screen is navigated to.
385
+ *
386
+ * @example
387
+ * ```js
388
+ * UNSTABLE_loader: ({ params }) => loadProfile(params.id),
389
+ * ```
390
+ */
391
+ UNSTABLE_loader?: (options: {
392
+ name: string;
393
+ params: AnyToUnknown<Params>;
394
+ }) => Promise<void>;
380
395
  };
381
396
 
382
397
  export type StaticScreenFactory<in out Bag extends NavigatorTypeBagBase> = <
383
398
  const Linking extends StaticScreenConfigLinking,
384
399
  const Screen extends StaticScreenConfigScreen,
400
+ const ProvidedOptions extends
401
+ | Bag['ScreenOptions']
402
+ | ((...args: never[]) => unknown) = Bag['ScreenOptions'],
385
403
  >(
386
404
  config: StaticScreenConfig<
387
405
  Linking,
@@ -390,7 +408,10 @@ export type StaticScreenFactory<in out Bag extends NavigatorTypeBagBase> = <
390
408
  Bag['ScreenOptions'],
391
409
  Bag['EventMap'],
392
410
  Bag['NavigationList'][keyof Bag['ParamList']]
393
- >
411
+ > & {
412
+ options?: ProvidedOptions &
413
+ NoExcessOptions<ProvidedOptions, Bag['ScreenOptions']>;
414
+ }
394
415
  ) => StaticScreenConfig<
395
416
  Linking,
396
417
  Screen,
@@ -456,51 +477,33 @@ type StaticConfigGroup<
456
477
  >;
457
478
  };
458
479
 
459
- export type StaticConfig<Bag extends NavigatorTypeBagBase> =
460
- StaticConfigInternal<
480
+ export type StaticConfig<
481
+ Bag extends NavigatorTypeBagBase,
482
+ Screens = StaticConfigScreens<
461
483
  Bag['ParamList'],
462
484
  Bag['State'],
463
485
  Bag['ScreenOptions'],
464
486
  Bag['EventMap'],
465
- Bag['NavigationList'],
466
- Bag['Navigator']
467
- >;
468
-
469
- type StaticConfigInternal<
470
- ParamList extends ParamListBase,
471
- State extends NavigationState,
472
- ScreenOptions extends {},
473
- EventMap extends EventMapBase,
474
- NavigationList extends NavigationListBase<ParamList>,
475
- Navigator extends React.ComponentType<any>,
476
- Screens = StaticConfigScreens<
477
- ParamList,
478
- State,
479
- ScreenOptions,
480
- EventMap,
481
- NavigationList
487
+ Bag['NavigationList']
482
488
  >,
483
489
  Groups = {
484
490
  [key: string]: StaticConfigGroup<
485
- ParamList,
486
- State,
487
- ScreenOptions,
488
- EventMap,
489
- NavigationList
491
+ Bag['ParamList'],
492
+ Bag['State'],
493
+ Bag['ScreenOptions'],
494
+ Bag['EventMap'],
495
+ Bag['NavigationList']
490
496
  >;
491
497
  },
492
498
  > = Omit<
493
- Omit<
494
- React.ComponentProps<Navigator>,
495
- keyof DefaultNavigatorOptions<any, any, any, any, any>
496
- > &
497
- DefaultNavigatorOptions<
498
- ParamList,
499
- State,
500
- ScreenOptions,
501
- EventMap,
502
- NavigationList[keyof ParamList]
503
- >,
499
+ NavigatorProps<
500
+ Bag['ParamList'],
501
+ Bag['State'],
502
+ Bag['ScreenOptions'],
503
+ Bag['EventMap'],
504
+ Bag['NavigationList'][keyof Bag['ParamList']],
505
+ Bag['Navigator']
506
+ >,
504
507
  'screens' | 'children'
505
508
  > &
506
509
  (
@@ -526,6 +529,67 @@ type StaticConfigInternal<
526
529
  }
527
530
  );
528
531
 
532
+ type NoExcessOptions<Provided, Allowed extends {}> = Provided extends (
533
+ ...args: never[]
534
+ ) => infer Return
535
+ ? (...args: never[]) => Allowed & NoExcessObject<Return, Allowed>
536
+ : NoExcessObject<Provided, Allowed>;
537
+
538
+ type NoExcessScreen<Screen, ScreenOptions extends {}> = Screen extends {
539
+ screen: unknown;
540
+ options: unknown;
541
+ }
542
+ ? { options: NoExcessOptions<Screen['options'], ScreenOptions> }
543
+ : unknown;
544
+
545
+ type NoExcessScreens<Screens, ScreenOptions extends {}> = {
546
+ [RouteName in keyof Screens]: NoExcessScreen<
547
+ Screens[RouteName],
548
+ ScreenOptions
549
+ >;
550
+ };
551
+
552
+ type NoExcessGroups<
553
+ Groups,
554
+ Bag extends NavigatorTypeBagBase,
555
+ ScreenOptions extends {},
556
+ > = {
557
+ [GroupName in keyof Groups]: NoExcessObject<
558
+ Groups[GroupName],
559
+ StaticConfigGroup<
560
+ ParamListBase,
561
+ NavigationState,
562
+ {},
563
+ EventMapBase,
564
+ NavigationListBase<ParamListBase>
565
+ >
566
+ > &
567
+ NoExcessStaticConfig<Groups[GroupName], Bag, ScreenOptions>;
568
+ };
569
+
570
+ // Each property is probed with `Config extends { key: infer X }` and the results are intersected.
571
+ // These conditionals stay deferred while the config is inferred, so they don't interfere with the inference.
572
+ // Indexed access (`Config['screens']`) or a mapped type over the whole config breaks the param list inference.
573
+ export type NoExcessStaticConfig<
574
+ Config,
575
+ Bag extends NavigatorTypeBagBase,
576
+ ScreenOptions extends {} = Bag['ScreenOptions'],
577
+ > = (Config extends { screenOptions: infer ProvidedOptions }
578
+ ? {
579
+ screenOptions: NoExcessOptions<ProvidedOptions, ScreenOptions>;
580
+ }
581
+ : unknown) &
582
+ (Config extends { screens: infer Screens }
583
+ ? {
584
+ screens: NoExcessScreens<Screens, ScreenOptions>;
585
+ }
586
+ : unknown) &
587
+ (Config extends { groups: infer Groups }
588
+ ? {
589
+ groups: NoExcessGroups<Groups, Bag, ScreenOptions>;
590
+ }
591
+ : unknown);
592
+
529
593
  /**
530
594
  * Props for a screen component which is rendered by a static navigator.
531
595
  * Takes the route params as a generic argument.
@@ -534,7 +598,7 @@ export type StaticScreenProps<
534
598
  in out T extends Record<string, unknown> | undefined,
535
599
  > = {
536
600
  route: {
537
- params: T;
601
+ readonly params: T;
538
602
  };
539
603
  };
540
604
 
@@ -724,14 +788,16 @@ export function createComponentForStaticConfig<
724
788
  : { ...rest.screenListeners, ...props.screenListeners };
725
789
 
726
790
  return (
727
- <Navigator
728
- {...rest}
729
- {...props}
730
- screenOptions={screenOptions}
731
- screenListeners={screenListeners}
732
- >
733
- {children}
734
- </Navigator>
791
+ <StaticTreeContext.Provider value={tree}>
792
+ <Navigator
793
+ {...rest}
794
+ {...props}
795
+ screenOptions={screenOptions}
796
+ screenListeners={screenListeners}
797
+ >
798
+ {children}
799
+ </Navigator>
800
+ </StaticTreeContext.Provider>
735
801
  );
736
802
  };
737
803
 
@@ -740,7 +806,7 @@ export function createComponentForStaticConfig<
740
806
  return NavigatorComponent;
741
807
  }
742
808
 
743
- type TreeForPathConfig = {
809
+ export type TreeForPathConfig = {
744
810
  config: ConfigForPathConfig;
745
811
  };
746
812
 
@@ -752,11 +818,7 @@ type ConfigForPathConfig = {
752
818
 
753
819
  type LinkingForPathConfig =
754
820
  | string
755
- | (Partial<StaticScreenConfigLinkingAlias> & {
756
- alias?: (string | StaticScreenConfigLinkingAlias)[];
757
- initialRouteName?: string | undefined;
758
- screens?: PathConfigMap<ParamListBase> | undefined;
759
- })
821
+ | (Partial<StaticScreenConfigLinkingAlias> & StaticScreenConfigLinkingExtras)
760
822
  | null
761
823
  | undefined;
762
824
 
@@ -766,7 +828,9 @@ type ScreenForPathConfig =
766
828
  | ScreenValueForPathConfig
767
829
  | {
768
830
  screen: ScreenValueForPathConfig;
831
+ initialParams?: object;
769
832
  linking?: LinkingForPathConfig;
833
+ UNSTABLE_loader?: unknown;
770
834
  };
771
835
 
772
836
  type PathConfigMapForStaticNavigation = Record<
@@ -0,0 +1,7 @@
1
+ import * as React from 'react';
2
+
3
+ import type { TreeForPathConfig } from './StaticNavigation';
4
+
5
+ export const StaticTreeContext = React.createContext<
6
+ TreeForPathConfig | undefined
7
+ >(undefined);