@react-navigation/core 8.0.0-alpha.22 → 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 (63) 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 +11 -14
  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 -33
  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 +0 -7
  38. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  39. package/lib/typescript/src/useNavigationHelpers.d.ts +0 -7
  40. package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
  41. package/lib/typescript/src/useOnAction.d.ts.map +1 -1
  42. package/lib/typescript/src/useOnPreventRemove.d.ts +2 -6
  43. package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
  44. package/lib/typescript/src/useRoute.d.ts +1 -1
  45. package/lib/typescript/src/useRoute.d.ts.map +1 -1
  46. package/lib/typescript/src/utilities.d.ts +4 -38
  47. package/lib/typescript/src/utilities.d.ts.map +1 -1
  48. package/lib/typescript/src/validatePathConfig.d.ts.map +1 -1
  49. package/package.json +5 -5
  50. package/src/NavigationBuilderContext.tsx +5 -1
  51. package/src/StaticNavigation.tsx +7 -6
  52. package/src/createNavigatorFactory.tsx +23 -2
  53. package/src/getPathFromState.tsx +97 -66
  54. package/src/getStateFromPath.tsx +168 -142
  55. package/src/index.tsx +1 -0
  56. package/src/types.tsx +257 -54
  57. package/src/useEventEmitter.tsx +6 -1
  58. package/src/useNavigationBuilder.tsx +11 -15
  59. package/src/useOnAction.tsx +6 -2
  60. package/src/useOnPreventRemove.tsx +68 -36
  61. package/src/useRoute.tsx +3 -5
  62. package/src/utilities.tsx +8 -41
  63. 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;
@@ -336,27 +375,6 @@ type NavigationHelpersCommon<
336
375
  : never
337
376
  ): void;
338
377
 
339
- /**
340
- * Navigate to a route in current navigation tree.
341
- *
342
- * @param options.name Name of the route to navigate to.
343
- * @param [options.params] Params object for the route.
344
- * @param [options.path] Path to associate the route with (e.g. for deep links).
345
- * @param [options.merge] Whether to merge the params onto the route. Defaults to `false`.
346
- * @param [options.pop] Whether to pop routes in a stack to go back to the matching route. Defaults to `false`.
347
- */
348
- navigate<RouteName extends keyof ParamList>(
349
- options: RouteName extends unknown
350
- ? {
351
- name: RouteName;
352
- params: ParamList[RouteName];
353
- path?: string | undefined;
354
- merge?: boolean | undefined;
355
- pop?: boolean | undefined;
356
- }
357
- : never
358
- ): void;
359
-
360
378
  /**
361
379
  * Preloads the route in current navigation tree.
362
380
  *
@@ -933,15 +951,19 @@ export type NavigationContainerEventMap = {
933
951
  /**
934
952
  * The data object passed when emitting the event.
935
953
  */
936
- data: unknown;
954
+ data?: unknown;
937
955
  /**
938
956
  * The key of the target route which received the event.
939
957
  */
940
- target: string | undefined;
958
+ target?: string | undefined;
941
959
  /**
942
960
  * Whether `event.preventDefault()` was called on this event.
943
961
  */
944
- defaultPrevented: boolean | undefined;
962
+ defaultPrevented?: boolean | undefined;
963
+ /**
964
+ * Prevent the default action which happens on this event.
965
+ */
966
+ preventDefault?: (() => void) | undefined;
945
967
  };
946
968
  };
947
969
  };
@@ -1021,8 +1043,8 @@ export type RouteForName<
1021
1043
  // Look up route object by checking each level and recursing into the nested param lists
1022
1044
  // This avoids building a union of all routes, which doesn't scale on large trees
1023
1045
  type RouteForNameInternal<ParamList extends {}, RouteName extends string> =
1024
- | (RouteName extends infer Name extends KeyOf<ParamList>
1025
- ? RouteProp<ParamList, Name>
1046
+ | (RouteName extends KeyOf<ParamList>
1047
+ ? RouteProp<ParamList, RouteName>
1026
1048
  : never)
1027
1049
  | RouteForNameNested<NestedParamLists<ParamList>, RouteName>;
1028
1050
 
@@ -1031,11 +1053,7 @@ type RouteForNameNested<ParamLists, RouteName extends string> =
1031
1053
  // e.g. if ParamLists is A | B
1032
1054
  // TypeScript evaluates the branch once per member (A, then B)
1033
1055
  // and unions the outcomes
1034
- // The infer ParamList extends {} just captures the current member
1035
- // and re-asserts the {} constraint that RouteForNameInternal requires
1036
- ParamLists extends infer ParamList extends {}
1037
- ? RouteForNameInternal<ParamList, RouteName>
1038
- : never;
1056
+ ParamLists extends {} ? RouteForNameInternal<ParamLists, RouteName> : never;
1039
1057
 
1040
1058
  /**
1041
1059
  * Union of param lists of the nested navigators in a param list.
@@ -1043,7 +1061,7 @@ type RouteForNameNested<ParamLists, RouteName extends string> =
1043
1061
  export type NestedParamLists<ParamList extends {}> = {
1044
1062
  [RouteName in keyof ParamList]: NavigatorScreenParams<{}> extends ParamList[RouteName]
1045
1063
  ? NotUndefined<ParamList[RouteName]> extends NavigatorScreenParams<infer T>
1046
- ? T
1064
+ ? ParamListForNavigator<T>
1047
1065
  : never
1048
1066
  : never;
1049
1067
  }[keyof ParamList];
@@ -1051,7 +1069,9 @@ export type NestedParamLists<ParamList extends {}> = {
1051
1069
  type ParamListRoute<ParamList extends {}> = {
1052
1070
  [RouteName in keyof ParamList]: NavigatorScreenParams<{}> extends ParamList[RouteName]
1053
1071
  ? NotUndefined<ParamList[RouteName]> extends NavigatorScreenParams<infer T>
1054
- ? ParamListRoute<T> | RouteProp<ParamList, RouteName>
1072
+ ?
1073
+ | ParamListRoute<ParamListForNavigator<T>>
1074
+ | RouteProp<ParamList, RouteName>
1055
1075
  : RouteProp<ParamList, RouteName>
1056
1076
  : RouteProp<ParamList, RouteName>;
1057
1077
  }[keyof ParamList];
@@ -1077,13 +1097,15 @@ type BasicNavigationList<
1077
1097
  ? NotUndefined<ParamList[RouteName]> extends NavigatorScreenParams<
1078
1098
  infer T
1079
1099
  >
1080
- ? keyof T extends ExcludedRouteNames
1081
- ? {}
1082
- : BasicNavigationList<
1083
- T,
1084
- ExcludedRouteNames,
1085
- NavigationProp<ParamList, RouteName>
1086
- >
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
+ : {}
1087
1109
  : {}
1088
1110
  : {}) &
1089
1111
  (RouteName extends ExcludedRouteNames
@@ -1097,6 +1119,105 @@ type BasicNavigationList<
1097
1119
  }[keyof ParamList]
1098
1120
  >;
1099
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
+
1100
1221
  export type NavigationListForNavigator<Navigator> =
1101
1222
  Navigator extends PrivateValueStore<infer Value> ? Value[1] : {};
1102
1223
 
@@ -1115,13 +1236,14 @@ type NavigationListForNestedInternal<
1115
1236
  Parent = undefined,
1116
1237
  NavigatorList extends Record<string, any> =
1117
1238
  NavigationListForNavigator<Navigator>,
1239
+ Navigators = NestedNavigatorsOf<Navigator>,
1118
1240
  // This navigator's own routes, each composed with the accumulated parent.
1119
1241
  // It's threaded into the recursion as the parent for nested navigators so
1120
1242
  // each route is composed with its ancestors exactly once, instead of
1121
1243
  // re-wrapping the whole descendant list at every level (which is quadratic
1122
1244
  // in the nesting depth).
1123
1245
  ComposedList = ComposeNavigationList<NavigatorList, Parent>,
1124
- > = Navigator extends {
1246
+ > = (Navigator extends {
1125
1247
  readonly config: {
1126
1248
  readonly screens?: infer Screens;
1127
1249
  readonly groups?: infer Groups;
@@ -1130,7 +1252,69 @@ type NavigationListForNestedInternal<
1130
1252
  ? ComposedList &
1131
1253
  NavigationListForScreens<ComposedList, Screens> &
1132
1254
  NavigationListForGroups<ComposedList, Groups>
1133
- : 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
+ : {};
1134
1318
 
1135
1319
  // Compose every entry in a navigator's list with the accumulated parent.
1136
1320
  // When there's no parent (root navigator), the list is returned as-is so the
@@ -1291,6 +1475,7 @@ type TypedNavigatorStatic<
1291
1475
  export type TypedNavigator<
1292
1476
  Bag extends NavigatorTypeBagBase,
1293
1477
  Config = unknown,
1478
+ Nesting = unknown,
1294
1479
  > = (undefined extends Config
1295
1480
  ? TypedNavigatorInternal<
1296
1481
  Bag['ParamList'],
@@ -1302,7 +1487,7 @@ export type TypedNavigator<
1302
1487
  >
1303
1488
  : TypedNavigatorStatic<Bag, Config>) &
1304
1489
  PrivateValueStore<
1305
- [Bag['ParamList'], Bag['NavigationList'], unknown, unknown]
1490
+ [Bag['ParamList'], Bag['NavigationList'], unknown, Nesting]
1306
1491
  >;
1307
1492
 
1308
1493
  type TypedNavigatorInternal<
@@ -1350,7 +1535,17 @@ type TypedNavigatorInternal<
1350
1535
  ) => null;
1351
1536
  };
1352
1537
 
1353
- 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 {}> =
1354
1549
  | {
1355
1550
  screen?: never;
1356
1551
  params?: never;
@@ -1385,6 +1580,10 @@ export type NavigatorScreenParams<ParamList extends {}> =
1385
1580
  };
1386
1581
  }[keyof ParamList];
1387
1582
 
1583
+ export type NavigatorScreenParams<ParamList extends {}> =
1584
+ NavigatorScreenParamsForParamList<ParamListForNavigator<ParamList>> &
1585
+ NavigatorScreenParamsBrand<ParamList>;
1586
+
1388
1587
  type ParseConfig<Params> = {
1389
1588
  [K in keyof Params]?:
1390
1589
  | ((value: string) => Params[K])
@@ -1452,16 +1651,20 @@ export type PathConfig<Params> = FlatType<
1452
1651
  */
1453
1652
  alias?: (string | PathConfigAlias<Params>)[] | undefined;
1454
1653
  } & (NonNullable<Params> extends NavigatorScreenParams<infer ParamList>
1455
- ? {
1456
- /**
1457
- * Path configuration for child screens.
1458
- */
1459
- screens?: PathConfigMap<ParamList> | undefined;
1460
- /**
1461
- * Name of the initial route to use for the navigator when the path matches.
1462
- */
1463
- initialRouteName?: Extract<keyof ParamList, string> | undefined;
1464
- }
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
+ : {}
1465
1668
  : {})
1466
1669
  >;
1467
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
  []
@@ -687,13 +687,16 @@ export function useNavigationBuilder<
687
687
  }
688
688
  } else {
689
689
  // If the route was updated with new screen name and/or params, we should navigate there
690
- action = CommonActions.navigate({
691
- name: route.params.screen,
692
- params: route.params.params,
693
- path: route.params.path,
694
- merge: route.params.merge,
695
- pop: route.params.pop,
696
- });
690
+ action = {
691
+ type: 'NAVIGATE',
692
+ payload: {
693
+ name: route.params.screen,
694
+ params: route.params.params,
695
+ path: route.params.path,
696
+ merge: route.params.merge,
697
+ pop: route.params.pop,
698
+ },
699
+ } as const;
697
700
  }
698
701
  }
699
702
 
@@ -874,14 +877,7 @@ export function useNavigationBuilder<
874
877
 
875
878
  listeners.forEach((listener) => listener?.(e));
876
879
  }
877
-
878
- onEmitEvent({
879
- type: e.type,
880
- data: e.data,
881
- target: e.target,
882
- defaultPrevented: e.defaultPrevented,
883
- });
884
- });
880
+ }, onEmitEvent);
885
881
 
886
882
  useFocusEvents({ state, emitter });
887
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.