@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
@@ -43,6 +43,7 @@ type RouteConfig = {
43
43
  routeNames: string[];
44
44
  parse?: ParseConfig | undefined;
45
45
  explicitParamNames?: Set<string> | undefined;
46
+ pathParamNames: Set<string>;
46
47
  hasNestedScreens: boolean;
47
48
  shared: boolean;
48
49
  };
@@ -202,23 +203,21 @@ export function getStateFromPath<ParamList extends {}>(
202
203
  remaining = remaining.replace(normalizedPrefix, '');
203
204
  }
204
205
 
205
- const decodedSegments: string[] = [];
206
+ if (screens === undefined) {
207
+ // When no config is specified, use the path segments as route names
208
+ const routes: ParsedRoute[] = [];
206
209
 
207
- for (const segment of remaining.split('/')) {
208
- if (!segment) {
209
- continue;
210
- }
210
+ for (const segment of remaining.split('/')) {
211
+ if (!segment) {
212
+ continue;
213
+ }
211
214
 
212
- try {
213
- decodedSegments.push(decodeURIComponent(segment));
214
- } catch {
215
- return undefined;
215
+ try {
216
+ routes.push({ name: decodeURIComponent(segment) });
217
+ } catch {
218
+ return undefined;
219
+ }
216
220
  }
217
- }
218
-
219
- if (screens === undefined) {
220
- // When no config is specified, use the path segments as route names
221
- const routes = decodedSegments.map((name) => ({ name }));
222
221
 
223
222
  if (routes.length) {
224
223
  return createNestedStateObject(path, routes, initialRoutes);
@@ -248,7 +247,29 @@ export function getStateFromPath<ParamList extends {}>(
248
247
 
249
248
  // We match the whole path against the regex instead of segments
250
249
  // This makes sure matches such as wildcard will catch any unmatched routes, even if nested
250
+ const firstRawSegment = remaining.split('/')[0];
251
+
252
+ let firstDecodedSegment: string | undefined;
253
+
254
+ try {
255
+ firstDecodedSegment =
256
+ firstRawSegment != null ? decodeURIComponent(firstRawSegment) : undefined;
257
+ } catch {
258
+ firstDecodedSegment = undefined;
259
+ }
260
+
251
261
  for (const config of configs) {
262
+ // Regex matching is expensive, so skip configs that can't match this path.
263
+ if (
264
+ !canMatchFirstSegment(
265
+ config.segments[0],
266
+ firstDecodedSegment,
267
+ firstRawSegment
268
+ )
269
+ ) {
270
+ continue;
271
+ }
272
+
252
273
  const routes = matchAgainstConfig(remaining, config, configsByScreen);
253
274
 
254
275
  if (routes === undefined) {
@@ -287,6 +308,26 @@ export function getStateFromPath<ParamList extends {}>(
287
308
  return undefined;
288
309
  }
289
310
 
311
+ const canMatchFirstSegment = (
312
+ configSegment: string | undefined,
313
+ decodedSegment: string | undefined,
314
+ rawSegment: string | undefined
315
+ ) => {
316
+ if (decodedSegment === undefined) {
317
+ return true;
318
+ }
319
+
320
+ if (configSegment === undefined) {
321
+ return false;
322
+ }
323
+
324
+ if (configSegment === '*' || configSegment.startsWith(':')) {
325
+ return true;
326
+ }
327
+
328
+ return configSegment === decodedSegment || configSegment === rawSegment;
329
+ };
330
+
290
331
  /**
291
332
  * Reference to the last used config resources. This is used to avoid recomputing the config resources when the options are the same.
292
333
  */
@@ -295,11 +336,17 @@ const cachedConfigResources = new WeakMap<Options<{}>, ConfigResources>();
295
336
  function getConfigResources<ParamList extends {}>(
296
337
  options: Options<ParamList> | undefined
297
338
  ) {
298
- if (!options) return prepareConfigResources();
339
+ if (!options) {
340
+ return prepareConfigResources();
341
+ }
299
342
 
300
343
  const cached = cachedConfigResources.get(options);
301
344
 
302
- if (cached) return cached;
345
+ if (cached) {
346
+ return cached;
347
+ }
348
+
349
+ validatePathConfig(options);
303
350
 
304
351
  const resources = prepareConfigResources(options);
305
352
 
@@ -309,21 +356,23 @@ function getConfigResources<ParamList extends {}>(
309
356
  }
310
357
 
311
358
  function prepareConfigResources(options?: Options<{}>) {
312
- if (options) {
313
- validatePathConfig(options);
314
- }
315
-
316
359
  const initialRoutes = getInitialRoutes(options);
317
360
  const configs = getSortedNormalizedConfigs(initialRoutes, options?.screens);
318
361
 
319
- checkForDuplicatedConfigs(configs);
320
-
321
362
  const configsByScreen: Record<string, RouteConfig[]> = {};
322
363
  const configsByPattern: Record<string, RouteConfig[]> = {};
323
364
 
324
365
  for (const c of configs) {
325
366
  (configsByScreen[c.screen] ??= []).push(c);
326
- (configsByPattern[c.pattern] ??= []).push(c);
367
+
368
+ const existing = configsByPattern[c.pattern];
369
+
370
+ if (existing) {
371
+ checkForDuplicatedConfigs(existing[existing.length - 1], c);
372
+ existing.push(c);
373
+ } else {
374
+ configsByPattern[c.pattern] = [c];
375
+ }
327
376
  }
328
377
 
329
378
  return {
@@ -453,37 +502,31 @@ function getSortedNormalizedConfigs(
453
502
  });
454
503
  }
455
504
 
456
- function checkForDuplicatedConfigs(configs: RouteConfig[]) {
457
- // Check for duplicate patterns in the config
458
- const patterns: Record<string, RouteConfig> = {};
459
-
460
- for (const config of configs) {
461
- const pattern = config.pattern;
462
- const existing = patterns[pattern];
463
-
464
- if (existing) {
465
- const a = existing.routeNames;
466
- const b = config.routeNames;
467
-
468
- // It's not a problem if the path string omitted from a inner most screen
469
- // For example, it's ok if a path resolves to `A > B > C` or `A > B`
470
- const intersects =
471
- a.length > b.length
472
- ? b.every((it, i) => a[i] === it)
473
- : a.every((it, i) => b[i] === it);
474
-
475
- if (!intersects && (!existing.shared || !config.shared)) {
476
- throw new Error(
477
- `Found conflicting screens with the same pattern. The pattern '${
478
- pattern
479
- }' resolves to both '${a.join(' > ')}' and '${b.join(
480
- ' > '
481
- )}'. Patterns must be unique and cannot resolve to more than one screen unless shared: true is specified.`
482
- );
483
- }
484
- }
505
+ // Throw if two configs resolve to the same pattern but conflicting screens
506
+ function checkForDuplicatedConfigs(
507
+ existing: RouteConfig | undefined,
508
+ config: RouteConfig
509
+ ) {
510
+ if (!existing) {
511
+ return;
512
+ }
485
513
 
486
- patterns[pattern] = config;
514
+ const a = existing.routeNames;
515
+ const b = config.routeNames;
516
+
517
+ // It's not a problem if the path string omitted from a inner most screen
518
+ // For example, it's ok if a path resolves to `A > B > C` or `A > B`
519
+ const intersects =
520
+ a.length > b.length ? arrayStartsWith(a, b) : arrayStartsWith(b, a);
521
+
522
+ if (!intersects && (!existing.shared || !config.shared)) {
523
+ throw new Error(
524
+ `Found conflicting screens with the same pattern. The pattern '${
525
+ config.pattern
526
+ }' resolves to both '${a.join(' > ')}' and '${b.join(
527
+ ' > '
528
+ )}'. Patterns must be unique and cannot resolve to more than one screen unless shared: true is specified.`
529
+ );
487
530
  }
488
531
  }
489
532
 
@@ -521,15 +564,13 @@ const matchAgainstConfig = (
521
564
  if (routeConfig && match.groups) {
522
565
  params = {};
523
566
 
524
- for (const key in match.groups) {
525
- const value = match.groups[key];
526
- const index = Number(key.replace(PARAM_GROUP_PREFIX, ''));
527
- const param = routeConfig.params.find((it) => it.index === index);
528
-
529
- if (!param || param.screen !== routeName) {
567
+ for (const param of routeConfig.params) {
568
+ if (param.screen !== routeName) {
530
569
  continue;
531
570
  }
532
571
 
572
+ const value = match.groups[`${PARAM_GROUP_PREFIX}${param.index}`];
573
+
533
574
  if (value == null) {
534
575
  params[param.name] = undefined;
535
576
  hasParams = true;
@@ -748,6 +789,7 @@ const createConfigItem = (
748
789
 
749
790
  const segments: string[] = [];
750
791
  const params: RouteConfig['params'] = [];
792
+ const pathParamNames = new Set<string>();
751
793
 
752
794
  if (parts.length) {
753
795
  let regexString = '^(';
@@ -765,6 +807,10 @@ const createConfigItem = (
765
807
  screen: part.screen,
766
808
  name: part.param,
767
809
  });
810
+
811
+ if (part.screen === screen) {
812
+ pathParamNames.add(part.param);
813
+ }
768
814
  } else {
769
815
  regexString += `${part.segment === '*' ? '.*' : escape(part.segment)}\\/`;
770
816
  }
@@ -788,6 +834,7 @@ const createConfigItem = (
788
834
  routeNames,
789
835
  parse,
790
836
  explicitParamNames: getExplicitParamNames(parse),
837
+ pathParamNames,
791
838
  hasNestedScreens,
792
839
  shared,
793
840
  };
@@ -1006,16 +1053,10 @@ const createNestedStateObject = (
1006
1053
 
1007
1054
  focusedRoute.path = path.replace(/\/$/, '');
1008
1055
 
1009
- const pathParamNames = new Set(
1010
- routeConfig?.params
1011
- .filter((param) => param.screen === routeConfig.screen)
1012
- .map((param) => param.name)
1013
- );
1014
-
1015
1056
  const queryParams = parseQueryParams(
1016
1057
  path,
1017
1058
  routeConfig?.parse,
1018
- pathParamNames,
1059
+ routeConfig?.pathParamNames,
1019
1060
  routeConfig?.explicitParamNames,
1020
1061
  routeConfig?.hasNestedScreens,
1021
1062
  focusedRoute.params
package/src/index.tsx CHANGED
@@ -4,6 +4,7 @@ export {
4
4
  createNavigatorFactory,
5
5
  type TypedNavigatorFactory,
6
6
  } from './createNavigatorFactory';
7
+ export { getLoaderForState as UNSTABLE_getLoaderForState } from './DataLoading';
7
8
  export { findFocusedRoute } from './findFocusedRoute';
8
9
  export { getActionFromState } from './getActionFromState';
9
10
  export { getFocusedRouteNameFromRoute } from './getFocusedRouteNameFromRoute';
@@ -59,6 +60,7 @@ export type {
59
60
  NavigatorScreenParams,
60
61
  NavigatorTypeBagBase,
61
62
  NavigatorTypeBagFor,
63
+ NestedNavigatorsForParamList,
62
64
  PathConfig,
63
65
  PathConfigMap,
64
66
  RootNavigator,
package/src/types.tsx CHANGED
@@ -93,7 +93,7 @@ export type DefaultNavigatorOptions<
93
93
  ScreenOptions,
94
94
  EventMap
95
95
  >,
96
- RouteProp<ParamList>
96
+ ScreenRouteProp<ParamList>
97
97
  >
98
98
  >;
99
99
  children: React.ReactNode;
@@ -107,7 +107,7 @@ export type DefaultNavigatorOptions<
107
107
  | (
108
108
  | ScreenListeners<State, EventMap>
109
109
  | ((props: {
110
- route: RouteProp<ParamList>;
110
+ route: ScreenRouteProp<ParamList>;
111
111
  navigation: Navigation;
112
112
  }) => ScreenListeners<State, EventMap>)
113
113
  )
@@ -120,7 +120,7 @@ export type DefaultNavigatorOptions<
120
120
  | (
121
121
  | ScreenOptions
122
122
  | ((props: {
123
- route: RouteProp<ParamList>;
123
+ route: ScreenRouteProp<ParamList>;
124
124
  navigation: Navigation;
125
125
  theme: Theme;
126
126
  }) => ScreenOptions)
@@ -429,7 +429,7 @@ type NavigationHelpersCommon<
429
429
 
430
430
  type ParamType<
431
431
  ParamList extends {},
432
- RouteName extends keyof ParamList | unknown,
432
+ RouteName,
433
433
  IsPartial extends boolean = false,
434
434
  > = RouteName extends keyof ParamList
435
435
  ? ParamList[RouteName] extends undefined
@@ -569,6 +569,13 @@ export type RouteProp<
569
569
  in out RouteName extends keyof ParamList = KeyOf<ParamList>,
570
570
  > = Route<Extract<RouteName, string>, ParamList[RouteName]>;
571
571
 
572
+ type ScreenRouteProp<
573
+ ParamList extends ParamListBase,
574
+ RouteName extends keyof ParamList = KeyOf<ParamList>,
575
+ > = {
576
+ [Name in Extract<RouteName, string>]: RouteProp<ParamList, Name>;
577
+ }[Extract<RouteName, string>];
578
+
572
579
  export type CompositeNavigationProp<
573
580
  A extends NavigationProp<ParamListBase, any, any, any, any>,
574
581
  B extends NavigationProp<ParamListBase, any, any, any, any>,
@@ -662,7 +669,7 @@ export type ScreenLayoutArgs<
662
669
  in out ScreenOptions extends {},
663
670
  in out Navigation,
664
671
  > = {
665
- route: RouteProp<ParamList, RouteName>;
672
+ route: ScreenRouteProp<ParamList, RouteName>;
666
673
  options: ScreenOptions;
667
674
  navigation: Navigation;
668
675
  theme: Theme;
@@ -724,16 +731,16 @@ export type RouteConfigComponent<
724
731
  * React component to render for this screen.
725
732
  */
726
733
  component: ScreenComponentType<ParamList, RouteName>;
727
- getComponent?: never | undefined;
728
- children?: never | undefined;
734
+ getComponent?: never;
735
+ children?: never;
729
736
  }
730
737
  | {
731
738
  /**
732
739
  * Lazily get a React component to render for this screen.
733
740
  */
734
741
  getComponent: () => ScreenComponentType<ParamList, RouteName>;
735
- component?: never | undefined;
736
- children?: never | undefined;
742
+ component?: never;
743
+ children?: never;
737
744
  }
738
745
  | {
739
746
  /**
@@ -743,8 +750,8 @@ export type RouteConfigComponent<
743
750
  route: RouteProp<ParamList, RouteName>;
744
751
  navigation: any;
745
752
  }) => React.ReactNode;
746
- component?: never | undefined;
747
- getComponent?: never | undefined;
753
+ component?: never;
754
+ getComponent?: never;
748
755
  };
749
756
 
750
757
  export type RouteConfigProps<
@@ -860,7 +867,7 @@ export type RouteGroupConfig<
860
867
  | (
861
868
  | ScreenOptions
862
869
  | ((props: {
863
- route: RouteProp<ParamList, keyof ParamList>;
870
+ route: ScreenRouteProp<ParamList>;
864
871
  navigation: Navigation;
865
872
  theme: Theme;
866
873
  }) => ScreenOptions)
@@ -1147,27 +1154,18 @@ type NavigationListForScreenParams<
1147
1154
  RouteName extends keyof ParamList,
1148
1155
  ExcludedRouteNames,
1149
1156
  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
- >;
1157
+ > = T extends PrivateValueStore<[any, any, any, any]> & NavigatorLike
1158
+ ? NavigationListForNestedInternal<
1159
+ T,
1160
+ GenericNavigationComposite<ParamList, RouteName, Parent>
1161
+ >
1162
+ : NavigationListForParamListScreenParams<
1163
+ T,
1164
+ ParamList,
1165
+ RouteName,
1166
+ ExcludedRouteNames,
1167
+ Parent
1168
+ >;
1171
1169
 
1172
1170
  type NavigationListForParamListScreenParams<
1173
1171
  T extends {},
@@ -1435,20 +1433,39 @@ export type NavigatorTypeBagFor<
1435
1433
  ParamList extends {},
1436
1434
  > = TypeBag & { ParamList: ParamList };
1437
1435
 
1438
- type TypedNavigatorComponent<Bag extends NavigatorTypeBagBase> =
1436
+ export type NavigatorProps<
1437
+ ParamList extends ParamListBase,
1438
+ State extends NavigationState,
1439
+ ScreenOptions extends {},
1440
+ EventMap extends EventMapBase,
1441
+ Navigation,
1442
+ Navigator extends React.ComponentType<any>,
1443
+ > = Omit<
1444
+ React.ComponentProps<Navigator>,
1445
+ keyof DefaultNavigatorOptions<any, any, any, any, any>
1446
+ > &
1447
+ DefaultNavigatorOptions<
1448
+ ParamList,
1449
+ State,
1450
+ ScreenOptions,
1451
+ EventMap,
1452
+ Navigation
1453
+ >;
1454
+
1455
+ type TypedNavigatorStaticComponent<Bag extends NavigatorTypeBagBase> =
1439
1456
  React.ComponentType<
1440
- Omit<
1441
- React.ComponentProps<
1442
- TypedNavigatorInternal<
1457
+ Partial<
1458
+ Omit<
1459
+ NavigatorProps<
1443
1460
  Bag['ParamList'],
1444
1461
  Bag['State'],
1445
1462
  Bag['ScreenOptions'],
1446
1463
  Bag['EventMap'],
1447
- Bag['NavigationList'],
1464
+ Bag['NavigationList'][keyof Bag['ParamList']],
1448
1465
  Bag['Navigator']
1449
- >['Navigator']
1450
- >,
1451
- 'children'
1466
+ >,
1467
+ 'children'
1468
+ >
1452
1469
  >
1453
1470
  >;
1454
1471
 
@@ -1466,10 +1483,10 @@ type TypedNavigatorStatic<
1466
1483
  config: Config;
1467
1484
  with: (
1468
1485
  Component: React.ComponentType<{
1469
- Navigator: TypedNavigatorComponent<Bag>;
1486
+ Navigator: TypedNavigatorStaticComponent<Bag>;
1470
1487
  }>
1471
1488
  ) => TypedNavigatorStaticDecorated<Bag, Config>;
1472
- getComponent: () => TypedNavigatorComponent<Bag>;
1489
+ getComponent: () => TypedNavigatorStaticComponent<Bag>;
1473
1490
  };
1474
1491
 
1475
1492
  export type TypedNavigator<
@@ -1502,17 +1519,14 @@ type TypedNavigatorInternal<
1502
1519
  * Navigator component which manages the child screens.
1503
1520
  */
1504
1521
  Navigator: React.ComponentType<
1505
- Omit<
1506
- React.ComponentProps<Navigator>,
1507
- keyof DefaultNavigatorOptions<any, any, any, any, any>
1508
- > &
1509
- DefaultNavigatorOptions<
1510
- ParamList,
1511
- State,
1512
- ScreenOptions,
1513
- EventMap,
1514
- NavigationList[keyof ParamList]
1515
- >
1522
+ NavigatorProps<
1523
+ ParamList,
1524
+ State,
1525
+ ScreenOptions,
1526
+ EventMap,
1527
+ NavigationList[keyof ParamList],
1528
+ Navigator
1529
+ >
1516
1530
  >;
1517
1531
  /**
1518
1532
  * Component used for grouping multiple route configuration.
@@ -1632,7 +1646,12 @@ type PathConfigAlias<Params> = {
1632
1646
  };
1633
1647
 
1634
1648
  export type PathConfig<Params> = FlatType<
1635
- Partial<PathConfigAlias<Params>> & {
1649
+ Partial<Omit<PathConfigAlias<Params>, 'path'>> & {
1650
+ /**
1651
+ * Path string to match against.
1652
+ * e.g. `/users/:id` will match `/users/1` and extract `id` param as `1`.
1653
+ */
1654
+ path?: string | undefined;
1636
1655
  /**
1637
1656
  * An object mapping the param name to a function which converts the param value to a string.
1638
1657
  * By default, all params are converted to strings using `String(value)`.
@@ -19,7 +19,7 @@ export function useEventEmitter<T extends Record<string, any>>(
19
19
  const listenRef = React.useRef(listen);
20
20
  const onPostEmitRef = React.useRef(onPostEmit);
21
21
 
22
- React.useEffect(() => {
22
+ React.useInsertionEffect(() => {
23
23
  listenRef.current = listen;
24
24
  onPostEmitRef.current = onPostEmit;
25
25
  });
@@ -28,8 +28,10 @@ export function useKeyedChildListeners() {
28
28
  keyedListeners[type][key] = listener;
29
29
 
30
30
  return () => {
31
- // @ts-expect-error: according to ref stated above you can use `key` to index type
32
- keyedListeners[type][key] = undefined;
31
+ if (keyedListeners[type][key] === listener) {
32
+ // @ts-expect-error: according to ref stated above you can use `key` to index type
33
+ keyedListeners[type][key] = undefined;
34
+ }
33
35
  };
34
36
  },
35
37
  [keyedListeners]
@@ -1,10 +1,7 @@
1
1
  import * as React from 'react';
2
2
 
3
3
  import { NavigationContainerRefContext } from './NavigationContainerRefContext';
4
- import {
5
- NavigationContext,
6
- NavigationRouteNameContext,
7
- } from './NavigationProvider';
4
+ import { NavigationContext } from './NavigationProvider';
8
5
  import type {
9
6
  GenericNavigation,
10
7
  NavigationListForNavigator,
@@ -46,23 +43,7 @@ export function useNavigation(name?: string): unknown {
46
43
  return navigation ?? root;
47
44
  }
48
45
 
49
- const routeName = React.use(NavigationRouteNameContext);
50
-
51
- // Generally, this condition isn't needed as `getParent(name)` works for current screen
52
- // However, it'll throw if the hook is used in a preloaded screen
53
- if (routeName === name) {
54
- if (navigation === undefined) {
55
- throw new Error(
56
- `Couldn't find a navigation object for '${name}'. This is not expected.`
57
- );
58
- }
59
-
60
- return navigation;
61
- }
62
-
63
- // We get parent first so that `getParent(name)` is not called in preloaded screens
64
- // It's ok since `getParent` also works for current screen, so parent is not skipped
65
- const parent = navigation?.getParent()?.getParent(name);
46
+ const parent = navigation?.getParent(name);
66
47
 
67
48
  if (parent === undefined) {
68
49
  throw new Error(
@@ -583,7 +583,7 @@ export function useNavigationBuilder<
583
583
 
584
584
  const previousRouteKeyListRef = React.useRef(routeKeyList);
585
585
 
586
- React.useEffect(() => {
586
+ React.useInsertionEffect(() => {
587
587
  previousRouteKeyListRef.current = routeKeyList;
588
588
  });
589
589
 
@@ -789,19 +789,6 @@ export function useNavigationBuilder<
789
789
  // eslint-disable-next-line react-hooks/exhaustive-deps
790
790
  }, []);
791
791
 
792
- // In some cases (e.g. route names change), internal state might have changed
793
- // But it hasn't been committed yet, so hasn't propagated to the sync external store
794
- // During this time, we need to return the internal state in `getState`
795
- // Otherwise it can result in inconsistent state during render in children
796
- // To avoid this, we use a ref for render phase, and immediately clear it on commit
797
- const stateRef = React.useRef<State | null>(state);
798
-
799
- stateRef.current = state;
800
-
801
- React.useLayoutEffect(() => {
802
- stateRef.current = null;
803
- });
804
-
805
792
  const getState = useLatestCallback((): State => {
806
793
  const currentState = getCurrentState();
807
794
 
@@ -956,9 +943,9 @@ export function useNavigationBuilder<
956
943
  onAction,
957
944
  onUnhandledAction,
958
945
  getState,
946
+ state,
959
947
  emitter,
960
948
  router,
961
- stateRef,
962
949
  });
963
950
 
964
951
  useFocusedListenersChildrenAdapter({
@@ -1013,7 +1000,10 @@ export function useNavigationBuilder<
1013
1000
  return (
1014
1001
  <NavigationMetaContext.Provider value={undefined}>
1015
1002
  <NavigationHelpersContext.Provider value={navigation}>
1016
- <NavigationStateListenerProvider state={state}>
1003
+ <NavigationStateListenerProvider
1004
+ state={state}
1005
+ getState={navigation.getState}
1006
+ >
1017
1007
  <FocusedRouteKeyContext.Provider value={focusedRoute.key}>
1018
1008
  <PreventRemoveProvider>{element}</PreventRemoveProvider>
1019
1009
  </FocusedRouteKeyContext.Provider>