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

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 (60) hide show
  1. package/lib/module/NavigationBuilderContext.js.map +1 -1
  2. package/lib/module/StaticNavigation.js +1 -1
  3. package/lib/module/StaticNavigation.js.map +1 -1
  4. package/lib/module/createNavigatorFactory.js.map +1 -1
  5. package/lib/module/getPathFromState.js +87 -57
  6. package/lib/module/getPathFromState.js.map +1 -1
  7. package/lib/module/getStateFromPath.js +77 -46
  8. package/lib/module/getStateFromPath.js.map +1 -1
  9. package/lib/module/index.js.map +1 -1
  10. package/lib/module/types.js +13 -0
  11. package/lib/module/types.js.map +1 -1
  12. package/lib/module/useEventEmitter.js +4 -1
  13. package/lib/module/useEventEmitter.js.map +1 -1
  14. package/lib/module/useNavigationBuilder.js +1 -7
  15. package/lib/module/useNavigationBuilder.js.map +1 -1
  16. package/lib/module/useOnAction.js +4 -1
  17. package/lib/module/useOnAction.js.map +1 -1
  18. package/lib/module/useOnPreventRemove.js +35 -24
  19. package/lib/module/useOnPreventRemove.js.map +1 -1
  20. package/lib/module/useRoute.js.map +1 -1
  21. package/lib/module/validatePathConfig.js +50 -37
  22. package/lib/module/validatePathConfig.js.map +1 -1
  23. package/lib/typescript/src/NavigationBuilderContext.d.ts +2 -2
  24. package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
  25. package/lib/typescript/src/StaticNavigation.d.ts +3 -3
  26. package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
  27. package/lib/typescript/src/createNavigatorFactory.d.ts +4 -2
  28. package/lib/typescript/src/createNavigatorFactory.d.ts.map +1 -1
  29. package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
  30. package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
  31. package/lib/typescript/src/index.d.ts +1 -1
  32. package/lib/typescript/src/index.d.ts.map +1 -1
  33. package/lib/typescript/src/types.d.ts +66 -17
  34. package/lib/typescript/src/types.d.ts.map +1 -1
  35. package/lib/typescript/src/useEventEmitter.d.ts +1 -1
  36. package/lib/typescript/src/useEventEmitter.d.ts.map +1 -1
  37. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  38. package/lib/typescript/src/useOnAction.d.ts.map +1 -1
  39. package/lib/typescript/src/useOnPreventRemove.d.ts +2 -6
  40. package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
  41. package/lib/typescript/src/useRoute.d.ts +1 -1
  42. package/lib/typescript/src/useRoute.d.ts.map +1 -1
  43. package/lib/typescript/src/utilities.d.ts +4 -38
  44. package/lib/typescript/src/utilities.d.ts.map +1 -1
  45. package/lib/typescript/src/validatePathConfig.d.ts.map +1 -1
  46. package/package.json +5 -5
  47. package/src/NavigationBuilderContext.tsx +5 -1
  48. package/src/StaticNavigation.tsx +7 -6
  49. package/src/createNavigatorFactory.tsx +23 -2
  50. package/src/getPathFromState.tsx +97 -66
  51. package/src/getStateFromPath.tsx +168 -142
  52. package/src/index.tsx +1 -0
  53. package/src/types.tsx +257 -33
  54. package/src/useEventEmitter.tsx +6 -1
  55. package/src/useNavigationBuilder.tsx +1 -8
  56. package/src/useOnAction.tsx +6 -2
  57. package/src/useOnPreventRemove.tsx +68 -36
  58. package/src/useRoute.tsx +3 -5
  59. package/src/utilities.tsx +8 -41
  60. package/src/validatePathConfig.tsx +85 -58
package/src/types.tsx CHANGED
@@ -287,6 +287,45 @@ export class PrivateValueStore<T extends [any, any, any, any]> {
287
287
  protected ''?: T;
288
288
  }
289
289
 
290
+ type NavigatorLike =
291
+ | {
292
+ Navigator: React.ComponentType<any>;
293
+ Screen: unknown;
294
+ Group: unknown;
295
+ }
296
+ | {
297
+ config: unknown;
298
+ getComponent: () => React.ComponentType<any>;
299
+ };
300
+
301
+ type ParamListForNavigator<T extends {}> = T extends NavigatorLike
302
+ ? T extends PrivateValueStore<[infer ParamList extends {}, any, any, any]>
303
+ ? ParamList
304
+ : T
305
+ : T;
306
+
307
+ // Remove `undefined` before checking for nested navigator params.
308
+ // If nothing remains, avoid matching `never` against `NavigatorScreenParams`
309
+ type NestedNavigatorForScreenParams<T, Params = NotUndefined<T>> = [
310
+ Params,
311
+ ] extends [never]
312
+ ? never
313
+ : Params extends NavigatorScreenParams<infer Navigator>
314
+ ? Navigator extends PrivateValueStore<[any, any, any, any]>
315
+ ? Navigator extends NavigatorLike
316
+ ? Navigator
317
+ : never
318
+ : never
319
+ : never;
320
+
321
+ export type NestedNavigatorsForParamList<ParamList extends {}> = {
322
+ [RouteName in keyof ParamList as NestedNavigatorForScreenParams<
323
+ ParamList[RouteName]
324
+ > extends never
325
+ ? never
326
+ : RouteName]: NestedNavigatorForScreenParams<ParamList[RouteName]>;
327
+ };
328
+
290
329
  type NavigateOptions = {
291
330
  merge?: boolean | undefined;
292
331
  pop?: boolean | undefined;
@@ -912,15 +951,19 @@ export type NavigationContainerEventMap = {
912
951
  /**
913
952
  * The data object passed when emitting the event.
914
953
  */
915
- data: unknown;
954
+ data?: unknown;
916
955
  /**
917
956
  * The key of the target route which received the event.
918
957
  */
919
- target: string | undefined;
958
+ target?: string | undefined;
920
959
  /**
921
960
  * Whether `event.preventDefault()` was called on this event.
922
961
  */
923
- defaultPrevented: boolean | undefined;
962
+ defaultPrevented?: boolean | undefined;
963
+ /**
964
+ * Prevent the default action which happens on this event.
965
+ */
966
+ preventDefault?: (() => void) | undefined;
924
967
  };
925
968
  };
926
969
  };
@@ -1000,8 +1043,8 @@ export type RouteForName<
1000
1043
  // Look up route object by checking each level and recursing into the nested param lists
1001
1044
  // This avoids building a union of all routes, which doesn't scale on large trees
1002
1045
  type RouteForNameInternal<ParamList extends {}, RouteName extends string> =
1003
- | (RouteName extends infer Name extends KeyOf<ParamList>
1004
- ? RouteProp<ParamList, Name>
1046
+ | (RouteName extends KeyOf<ParamList>
1047
+ ? RouteProp<ParamList, RouteName>
1005
1048
  : never)
1006
1049
  | RouteForNameNested<NestedParamLists<ParamList>, RouteName>;
1007
1050
 
@@ -1010,11 +1053,7 @@ type RouteForNameNested<ParamLists, RouteName extends string> =
1010
1053
  // e.g. if ParamLists is A | B
1011
1054
  // TypeScript evaluates the branch once per member (A, then B)
1012
1055
  // and unions the outcomes
1013
- // The infer ParamList extends {} just captures the current member
1014
- // and re-asserts the {} constraint that RouteForNameInternal requires
1015
- ParamLists extends infer ParamList extends {}
1016
- ? RouteForNameInternal<ParamList, RouteName>
1017
- : never;
1056
+ ParamLists extends {} ? RouteForNameInternal<ParamLists, RouteName> : never;
1018
1057
 
1019
1058
  /**
1020
1059
  * Union of param lists of the nested navigators in a param list.
@@ -1022,7 +1061,7 @@ type RouteForNameNested<ParamLists, RouteName extends string> =
1022
1061
  export type NestedParamLists<ParamList extends {}> = {
1023
1062
  [RouteName in keyof ParamList]: NavigatorScreenParams<{}> extends ParamList[RouteName]
1024
1063
  ? NotUndefined<ParamList[RouteName]> extends NavigatorScreenParams<infer T>
1025
- ? T
1064
+ ? ParamListForNavigator<T>
1026
1065
  : never
1027
1066
  : never;
1028
1067
  }[keyof ParamList];
@@ -1030,7 +1069,9 @@ export type NestedParamLists<ParamList extends {}> = {
1030
1069
  type ParamListRoute<ParamList extends {}> = {
1031
1070
  [RouteName in keyof ParamList]: NavigatorScreenParams<{}> extends ParamList[RouteName]
1032
1071
  ? NotUndefined<ParamList[RouteName]> extends NavigatorScreenParams<infer T>
1033
- ? ParamListRoute<T> | RouteProp<ParamList, RouteName>
1072
+ ?
1073
+ | ParamListRoute<ParamListForNavigator<T>>
1074
+ | RouteProp<ParamList, RouteName>
1034
1075
  : RouteProp<ParamList, RouteName>
1035
1076
  : RouteProp<ParamList, RouteName>;
1036
1077
  }[keyof ParamList];
@@ -1056,13 +1097,15 @@ type BasicNavigationList<
1056
1097
  ? NotUndefined<ParamList[RouteName]> extends NavigatorScreenParams<
1057
1098
  infer T
1058
1099
  >
1059
- ? keyof T extends ExcludedRouteNames
1060
- ? {}
1061
- : BasicNavigationList<
1062
- T,
1063
- ExcludedRouteNames,
1064
- NavigationProp<ParamList, RouteName>
1065
- >
1100
+ ? ParamListForNavigator<T> extends infer NestedParamList extends {}
1101
+ ? keyof NestedParamList extends ExcludedRouteNames
1102
+ ? {}
1103
+ : BasicNavigationList<
1104
+ NestedParamList,
1105
+ ExcludedRouteNames,
1106
+ NavigationProp<ParamList, RouteName>
1107
+ >
1108
+ : {}
1066
1109
  : {}
1067
1110
  : {}) &
1068
1111
  (RouteName extends ExcludedRouteNames
@@ -1076,6 +1119,105 @@ type BasicNavigationList<
1076
1119
  }[keyof ParamList]
1077
1120
  >;
1078
1121
 
1122
+ type InheritedParentParams<ParentParamList, ParamList> = {
1123
+ [K in keyof ParentParamList as K extends keyof ParamList
1124
+ ? never
1125
+ : string extends K
1126
+ ? never
1127
+ : K]: ParentParamList[K];
1128
+ };
1129
+
1130
+ type GenericNavigationComposite<
1131
+ ParamList extends ParamListBase,
1132
+ RouteName extends keyof ParamList,
1133
+ Parent,
1134
+ > =
1135
+ Parent extends NavigationProp<any, any, any, any, any>
1136
+ ? ParamListOfNavigationProp<Parent> extends infer ParentParamList
1137
+ ? NavigationProp<
1138
+ ParamList & InheritedParentParams<ParentParamList, ParamList>,
1139
+ RouteName
1140
+ > & { getParent: Parent['getParent'] }
1141
+ : never
1142
+ : NavigationProp<ParamList, RouteName>;
1143
+
1144
+ type NavigationListForScreenParams<
1145
+ T extends {},
1146
+ ParamList extends ParamListBase,
1147
+ RouteName extends keyof ParamList,
1148
+ ExcludedRouteNames,
1149
+ Parent extends NavigationProp<any, any, any, any, any> | undefined,
1150
+ > =
1151
+ T extends PrivateValueStore<[any, any, any, any]>
1152
+ ? T extends NavigatorLike
1153
+ ? NavigationListForNestedInternal<
1154
+ T,
1155
+ GenericNavigationComposite<ParamList, RouteName, Parent>
1156
+ >
1157
+ : NavigationListForParamListScreenParams<
1158
+ T,
1159
+ ParamList,
1160
+ RouteName,
1161
+ ExcludedRouteNames,
1162
+ Parent
1163
+ >
1164
+ : NavigationListForParamListScreenParams<
1165
+ T,
1166
+ ParamList,
1167
+ RouteName,
1168
+ ExcludedRouteNames,
1169
+ Parent
1170
+ >;
1171
+
1172
+ type NavigationListForParamListScreenParams<
1173
+ T extends {},
1174
+ ParamList extends ParamListBase,
1175
+ RouteName extends keyof ParamList,
1176
+ ExcludedRouteNames,
1177
+ Parent extends NavigationProp<any, any, any, any, any> | undefined,
1178
+ > =
1179
+ // `T` is only ever a plain param list here, since navigators are routed to
1180
+ // `NavigationListForNestedInternal` by `NavigationListForScreenParams`.
1181
+ // So there's no navigator to unwrap with `ParamListForNavigator`.
1182
+ T extends ParamListBase
1183
+ ? BasicNavigationListForParamList<
1184
+ T,
1185
+ ExcludedRouteNames,
1186
+ GenericNavigationComposite<ParamList, RouteName, Parent>
1187
+ >
1188
+ : {};
1189
+
1190
+ type BasicNavigationListForParamList<
1191
+ ParamList extends ParamListBase,
1192
+ ExcludedRouteNames,
1193
+ Parent extends NavigationProp<any, any, any, any, any> | undefined,
1194
+ > = UnionToIntersection<
1195
+ {
1196
+ [RouteName in keyof ParamList]: (NavigatorScreenParams<{}> extends ParamList[RouteName]
1197
+ ? NotUndefined<ParamList[RouteName]> extends NavigatorScreenParams<
1198
+ infer T
1199
+ >
1200
+ ? NavigationListForScreenParams<
1201
+ T,
1202
+ ParamList,
1203
+ RouteName,
1204
+ ExcludedRouteNames,
1205
+ Parent
1206
+ >
1207
+ : {}
1208
+ : {}) &
1209
+ (RouteName extends ExcludedRouteNames
1210
+ ? {}
1211
+ : {
1212
+ [Key in RouteName]: GenericNavigationComposite<
1213
+ ParamList,
1214
+ RouteName,
1215
+ Parent
1216
+ >;
1217
+ });
1218
+ }[keyof ParamList]
1219
+ >;
1220
+
1079
1221
  export type NavigationListForNavigator<Navigator> =
1080
1222
  Navigator extends PrivateValueStore<infer Value> ? Value[1] : {};
1081
1223
 
@@ -1094,13 +1236,14 @@ type NavigationListForNestedInternal<
1094
1236
  Parent = undefined,
1095
1237
  NavigatorList extends Record<string, any> =
1096
1238
  NavigationListForNavigator<Navigator>,
1239
+ Navigators = NestedNavigatorsOf<Navigator>,
1097
1240
  // This navigator's own routes, each composed with the accumulated parent.
1098
1241
  // It's threaded into the recursion as the parent for nested navigators so
1099
1242
  // each route is composed with its ancestors exactly once, instead of
1100
1243
  // re-wrapping the whole descendant list at every level (which is quadratic
1101
1244
  // in the nesting depth).
1102
1245
  ComposedList = ComposeNavigationList<NavigatorList, Parent>,
1103
- > = Navigator extends {
1246
+ > = (Navigator extends {
1104
1247
  readonly config: {
1105
1248
  readonly screens?: infer Screens;
1106
1249
  readonly groups?: infer Groups;
@@ -1109,7 +1252,69 @@ type NavigationListForNestedInternal<
1109
1252
  ? ComposedList &
1110
1253
  NavigationListForScreens<ComposedList, Screens> &
1111
1254
  NavigationListForGroups<ComposedList, Groups>
1112
- : ComposedList;
1255
+ : ComposedList) &
1256
+ NavigationListForNestedNavigators<ComposedList, Navigators> &
1257
+ NavigationListForDynamicNestedParamLists<Navigator, ComposedList, Navigators>;
1258
+
1259
+ type NavigationListForDynamicNestedParamLists<
1260
+ Navigator,
1261
+ ParentList,
1262
+ Navigators,
1263
+ > = Navigator extends { readonly config: unknown }
1264
+ ? {}
1265
+ : Navigator extends PrivateValueStore<[infer ParamList, any, any, any]>
1266
+ ? NavigationListForNestedParamLists<ParentList, ParamList, Navigators>
1267
+ : {};
1268
+
1269
+ // Map of route name to nested navigator type
1270
+ // inferred from `NavigatorScreenParams<typeof Navigator>`
1271
+ type NestedNavigatorsOf<Navigator> =
1272
+ Navigator extends PrivateValueStore<[any, any, any, infer Nesting]>
1273
+ ? Nesting extends Record<string, any>
1274
+ ? Nesting
1275
+ : {}
1276
+ : {};
1277
+
1278
+ // Like `NavigationListForScreens`, but reads nested navigators from the brand
1279
+ // map instead of a static config.
1280
+ type NavigationListForNestedNavigators<ParentList, Navigators> =
1281
+ UnionToIntersection<
1282
+ {
1283
+ [K in keyof Navigators]: K extends keyof ParentList
1284
+ ? NavigationListForNestedInternal<Navigators[K], ParentList[K]>
1285
+ : {};
1286
+ }[keyof Navigators]
1287
+ >;
1288
+
1289
+ // Fallback for dynamic nested routes declared through NavigatorScreenParams<ParamList>.
1290
+ // Param-list branches expose generic navigation props, while nested navigator
1291
+ // entries still recover their concrete navigator props.
1292
+ type NavigationListForNestedParamLists<ParentList, ParamList, Navigators> =
1293
+ ParentList extends Record<string, any>
1294
+ ? ParamList extends ParamListBase
1295
+ ? UnionToIntersection<
1296
+ {
1297
+ [K in keyof ParamList]: K extends keyof Navigators
1298
+ ? {}
1299
+ : K extends keyof ParentList
1300
+ ? NavigatorScreenParams<{}> extends ParamList[K]
1301
+ ? NotUndefined<ParamList[K]> extends NavigatorScreenParams<
1302
+ infer T
1303
+ >
1304
+ ? NavigationListForScreenParams<
1305
+ T,
1306
+ ParamList,
1307
+ K,
1308
+ keyof ParentList,
1309
+ ParentList[K]
1310
+ >
1311
+ : {}
1312
+ : {}
1313
+ : {};
1314
+ }[keyof ParamList]
1315
+ >
1316
+ : {}
1317
+ : {};
1113
1318
 
1114
1319
  // Compose every entry in a navigator's list with the accumulated parent.
1115
1320
  // When there's no parent (root navigator), the list is returned as-is so the
@@ -1270,6 +1475,7 @@ type TypedNavigatorStatic<
1270
1475
  export type TypedNavigator<
1271
1476
  Bag extends NavigatorTypeBagBase,
1272
1477
  Config = unknown,
1478
+ Nesting = unknown,
1273
1479
  > = (undefined extends Config
1274
1480
  ? TypedNavigatorInternal<
1275
1481
  Bag['ParamList'],
@@ -1281,7 +1487,7 @@ export type TypedNavigator<
1281
1487
  >
1282
1488
  : TypedNavigatorStatic<Bag, Config>) &
1283
1489
  PrivateValueStore<
1284
- [Bag['ParamList'], Bag['NavigationList'], unknown, unknown]
1490
+ [Bag['ParamList'], Bag['NavigationList'], unknown, Nesting]
1285
1491
  >;
1286
1492
 
1287
1493
  type TypedNavigatorInternal<
@@ -1329,7 +1535,17 @@ type TypedNavigatorInternal<
1329
1535
  ) => null;
1330
1536
  };
1331
1537
 
1332
- export type NavigatorScreenParams<ParamList extends {}> =
1538
+ declare const NavigatorScreenParamsSymbol: unique symbol;
1539
+
1540
+ type NavigatorScreenParamsBrand<T extends {}> = {
1541
+ // Preserve the original generic for inference without affecting assignability.
1542
+ // - The symbol key hides the property from normal object usage
1543
+ // - The actual type is intersected for inference
1544
+ // - The `any` makes the property effectively `any` for assignability
1545
+ readonly [NavigatorScreenParamsSymbol]?: any & { readonly type: T };
1546
+ };
1547
+
1548
+ type NavigatorScreenParamsForParamList<ParamList extends {}> =
1333
1549
  | {
1334
1550
  screen?: never;
1335
1551
  params?: never;
@@ -1364,6 +1580,10 @@ export type NavigatorScreenParams<ParamList extends {}> =
1364
1580
  };
1365
1581
  }[keyof ParamList];
1366
1582
 
1583
+ export type NavigatorScreenParams<ParamList extends {}> =
1584
+ NavigatorScreenParamsForParamList<ParamListForNavigator<ParamList>> &
1585
+ NavigatorScreenParamsBrand<ParamList>;
1586
+
1367
1587
  type ParseConfig<Params> = {
1368
1588
  [K in keyof Params]?:
1369
1589
  | ((value: string) => Params[K])
@@ -1431,16 +1651,20 @@ export type PathConfig<Params> = FlatType<
1431
1651
  */
1432
1652
  alias?: (string | PathConfigAlias<Params>)[] | undefined;
1433
1653
  } & (NonNullable<Params> extends NavigatorScreenParams<infer ParamList>
1434
- ? {
1435
- /**
1436
- * Path configuration for child screens.
1437
- */
1438
- screens?: PathConfigMap<ParamList> | undefined;
1439
- /**
1440
- * Name of the initial route to use for the navigator when the path matches.
1441
- */
1442
- initialRouteName?: Extract<keyof ParamList, string> | undefined;
1443
- }
1654
+ ? ParamListForNavigator<ParamList> extends infer ChildParamList extends {}
1655
+ ? {
1656
+ /**
1657
+ * Path configuration for child screens.
1658
+ */
1659
+ screens?: PathConfigMap<ChildParamList> | undefined;
1660
+ /**
1661
+ * Name of the initial route to use for the navigator when the path matches.
1662
+ */
1663
+ initialRouteName?:
1664
+ | Extract<keyof ChildParamList, string>
1665
+ | undefined;
1666
+ }
1667
+ : {}
1444
1668
  : {})
1445
1669
  >;
1446
1670
 
@@ -13,12 +13,15 @@ type Listeners = Set<(e: any) => void>;
13
13
  * Hook to manage the event system used by the navigator to notify screens of various events.
14
14
  */
15
15
  export function useEventEmitter<T extends Record<string, any>>(
16
- listen?: (e: any) => void
16
+ listen?: (e: any) => void,
17
+ onPostEmit?: (e: any) => void
17
18
  ): NavigationEventEmitter<T> {
18
19
  const listenRef = React.useRef(listen);
20
+ const onPostEmitRef = React.useRef(onPostEmit);
19
21
 
20
22
  React.useEffect(() => {
21
23
  listenRef.current = listen;
24
+ onPostEmitRef.current = onPostEmit;
22
25
  });
23
26
 
24
27
  const listeners = React.useRef<Record<string, Record<string, Listeners>>>(
@@ -124,6 +127,8 @@ export function useEventEmitter<T extends Record<string, any>>(
124
127
 
125
128
  callbacks?.forEach((cb) => cb(event));
126
129
 
130
+ onPostEmitRef.current?.(event);
131
+
127
132
  return event as any;
128
133
  },
129
134
  []
@@ -877,14 +877,7 @@ export function useNavigationBuilder<
877
877
 
878
878
  listeners.forEach((listener) => listener?.(e));
879
879
  }
880
-
881
- onEmitEvent({
882
- type: e.type,
883
- data: e.data,
884
- target: e.target,
885
- defaultPrevented: e.defaultPrevented,
886
- });
887
- });
880
+ }, onEmitEvent);
888
881
 
889
882
  useFocusEvents({ state, emitter });
890
883
 
@@ -88,8 +88,6 @@ export function useOnAction<State extends NavigationState>({
88
88
  result === null && action.target === state.key ? state : result;
89
89
 
90
90
  if (result !== null) {
91
- onDispatchAction(action, state === result);
92
-
93
91
  if (state !== result) {
94
92
  const isPrevented = shouldPreventRemove(
95
93
  emitter,
@@ -100,10 +98,16 @@ export function useOnAction<State extends NavigationState>({
100
98
  );
101
99
 
102
100
  if (isPrevented) {
101
+ onDispatchAction(action, true);
102
+
103
103
  return true;
104
104
  }
105
105
 
106
+ onDispatchAction(action, false);
107
+
106
108
  setState(result);
109
+ } else {
110
+ onDispatchAction(action, true);
107
111
  }
108
112
 
109
113
  if (onRouteFocusParent !== undefined) {
@@ -1,6 +1,9 @@
1
1
  import type {
2
2
  NavigationAction,
3
+ NavigationRoute,
3
4
  NavigationState,
5
+ ParamListBase,
6
+ PartialRoute,
4
7
  } from '@react-navigation/routers';
5
8
  import * as React from 'react';
6
9
 
@@ -23,16 +26,18 @@ const VISITED_ROUTE_KEYS = Symbol('VISITED_ROUTE_KEYS');
23
26
  export const shouldPreventRemove = (
24
27
  emitter: NavigationEventEmitter<EventMapCore<any>>,
25
28
  beforeRemoveListeners: Record<string, ChildBeforeRemoveListener | undefined>,
26
- currentRoutes: { key: string }[],
27
- nextRoutes: { key?: string | undefined }[],
29
+ currentRoutes: NavigationRoute<ParamListBase, string>[],
30
+ nextRoutes: (
31
+ | NavigationRoute<ParamListBase, string>
32
+ | PartialRoute<NavigationRoute<ParamListBase, string>>
33
+ )[],
28
34
  action: NavigationAction
29
35
  ) => {
30
- const nextRouteKeys = nextRoutes.map((route) => route.key);
31
-
32
- // Call these in reverse order so last screens handle the event first
33
- const removedRoutes = currentRoutes
34
- .filter((route) => !nextRouteKeys.includes(route.key))
35
- .reverse();
36
+ const nextRoutesByKey = new Map(
37
+ nextRoutes
38
+ .map((route) => [route.key, route] as const)
39
+ .filter(([, route]) => route.key != null)
40
+ );
36
41
 
37
42
  const visitedRouteKeys: Set<string> =
38
43
  // @ts-expect-error: add this property to mark that we've already emitted this action
@@ -43,30 +48,53 @@ export const shouldPreventRemove = (
43
48
  [VISITED_ROUTE_KEYS]: visitedRouteKeys,
44
49
  };
45
50
 
46
- for (const route of removedRoutes) {
51
+ // Call these in reverse order so last screens handle the event first
52
+ const reversedCurrentRoutes = [...currentRoutes].reverse();
53
+
54
+ for (const route of reversedCurrentRoutes) {
47
55
  if (visitedRouteKeys.has(route.key)) {
48
56
  // Skip if we've already emitted this action for this screen
49
57
  continue;
50
58
  }
51
59
 
52
- // First, we need to check if any child screens want to prevent it
53
- const isPrevented = beforeRemoveListeners[route.key]?.(beforeRemoveAction);
54
-
55
- if (isPrevented) {
56
- return true;
57
- }
58
-
59
- visitedRouteKeys.add(route.key);
60
+ if (!nextRoutesByKey.has(route.key)) {
61
+ // The route is not in next state, so it's being removed
62
+ // First, we need to check if any child screens want to prevent it
63
+ const isPrevented = beforeRemoveListeners[route.key]?.(
64
+ beforeRemoveAction,
65
+ undefined
66
+ );
67
+
68
+ if (isPrevented) {
69
+ return true;
70
+ }
71
+
72
+ visitedRouteKeys.add(route.key);
73
+
74
+ const event = emitter.emit({
75
+ type: 'beforeRemove',
76
+ target: route.key,
77
+ data: { action: beforeRemoveAction },
78
+ canPreventDefault: true,
79
+ });
60
80
 
61
- const event = emitter.emit({
62
- type: 'beforeRemove',
63
- target: route.key,
64
- data: { action: beforeRemoveAction },
65
- canPreventDefault: true,
66
- });
81
+ if (event.defaultPrevented) {
82
+ return true;
83
+ }
84
+ } else {
85
+ // The route is kept but its nested state changed, so propagate the check into the nested navigator
86
+ const nextRoute = nextRoutesByKey.get(route.key);
87
+
88
+ if (route.state != null && route.state !== nextRoute?.state) {
89
+ const isPrevented = beforeRemoveListeners[route.key]?.(
90
+ beforeRemoveAction,
91
+ nextRoute?.state
92
+ );
67
93
 
68
- if (event.defaultPrevented) {
69
- return true;
94
+ if (isPrevented) {
95
+ return true;
96
+ }
97
+ }
70
98
  }
71
99
  }
72
100
 
@@ -84,17 +112,21 @@ export function useOnPreventRemove({
84
112
 
85
113
  React.useInsertionEffect(() => {
86
114
  if (routeKey) {
87
- return addKeyedListener?.('beforeRemove', routeKey, (action) => {
88
- const state = getState();
89
-
90
- return shouldPreventRemove(
91
- emitter,
92
- beforeRemoveListeners,
93
- state.routes,
94
- [],
95
- action
96
- );
97
- });
115
+ return addKeyedListener?.(
116
+ 'beforeRemove',
117
+ routeKey,
118
+ (action, nextState) => {
119
+ const state = getState();
120
+
121
+ return shouldPreventRemove(
122
+ emitter,
123
+ beforeRemoveListeners,
124
+ state.routes,
125
+ nextState?.routes ?? [],
126
+ action
127
+ );
128
+ }
129
+ );
98
130
  }
99
131
  }, [addKeyedListener, beforeRemoveListeners, emitter, getState, routeKey]);
100
132
  }
package/src/useRoute.tsx CHANGED
@@ -22,11 +22,9 @@ type AllRouteNames<ParamList extends {}> =
22
22
  | KeyOf<ParamList>
23
23
  | AllNestedRouteNames<NestedParamLists<ParamList>>;
24
24
 
25
- type AllNestedRouteNames<ParamLists> =
26
- // Distributes over the union of nested param lists
27
- ParamLists extends infer ParamList extends {}
28
- ? AllRouteNames<ParamList>
29
- : never;
25
+ type AllNestedRouteNames<ParamLists> = ParamLists extends {}
26
+ ? AllRouteNames<ParamLists>
27
+ : never;
30
28
 
31
29
  /**
32
30
  * Hook to access the route prop of the parent screen anywhere.
package/src/utilities.tsx CHANGED
@@ -25,8 +25,6 @@ export type UnionToIntersection<U> = (
25
25
  ? I
26
26
  : never;
27
27
 
28
- export type UnknownToUndefined<T> = unknown extends T ? undefined : T;
29
-
30
28
  /**
31
29
  * Exclude undefined from a type.
32
30
  * Similar to NonNullable but only excludes undefined, not null.
@@ -173,46 +171,15 @@ type QueryParamRequiredKeys<Params, Parse> = Exclude<
173
171
  QueryParamOptionalKeys<Params, Parse>
174
172
  >;
175
173
 
176
- /**
177
- * Infer the path string from a linking config.
178
- */
179
- export type InferPath<T> = T extends { path: infer P extends string }
180
- ? P
181
- : never;
182
-
183
- /**
184
- * Infer the parse functions from a linking config.
185
- */
186
- export type InferParse<T> = T extends { parse: infer P } ? P : {};
187
-
188
174
  export type InferParamsFromLinking<T> = T extends {
189
175
  path: infer P extends string;
176
+ parse: infer Parse;
190
177
  }
191
- ? UndefinedIfAllOptional<
192
- ExtractParamsType<ExtractParamStrings<P>, InferParse<T>>
193
- >
194
- : T extends string
195
- ? UndefinedIfAllOptional<
196
- ExtractParamsType<ExtractParamStrings<T>, undefined>
197
- >
198
- : undefined;
199
-
200
- /**
201
- * Infer the params type from a screen component or nested navigator.
202
- */
203
- export type InferScreenParams<T> =
204
- T extends React.ComponentType<{ route: { params: infer P } }>
205
- ? P
206
- : T extends { config: { screens: infer Screens } }
207
- ? import('./types').NavigatorScreenParams<{
208
- [K in keyof Screens]: Screens[K] extends React.ComponentType<{
209
- route: { params: infer P };
210
- }>
211
- ? P
212
- : Screens[K] extends { screen: infer S }
213
- ? S extends React.ComponentType<{ route: { params: infer P } }>
214
- ? P
215
- : undefined
216
- : undefined;
217
- }>
178
+ ? UndefinedIfAllOptional<ExtractParamsType<ExtractParamStrings<P>, Parse>>
179
+ : T extends {
180
+ path: infer P extends string;
181
+ }
182
+ ? UndefinedIfAllOptional<ExtractParamStrings<P>>
183
+ : T extends string
184
+ ? UndefinedIfAllOptional<ExtractParamStrings<T>>
218
185
  : undefined;