@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.
- package/lib/module/BaseNavigationContainer.js +5 -2
- package/lib/module/BaseNavigationContainer.js.map +1 -1
- package/lib/module/DataLoading.js +102 -0
- package/lib/module/DataLoading.js.map +1 -0
- package/lib/module/NavigationIndependentTree.js +7 -6
- package/lib/module/NavigationIndependentTree.js.map +1 -1
- package/lib/module/NavigationProvider.js +12 -12
- package/lib/module/NavigationProvider.js.map +1 -1
- package/lib/module/PreventRemoveContext.js.map +1 -1
- package/lib/module/PreventRemoveProvider.js +34 -24
- package/lib/module/PreventRemoveProvider.js.map +1 -1
- package/lib/module/StaticNavigation.js +14 -6
- package/lib/module/StaticNavigation.js.map +1 -1
- package/lib/module/StaticTreeContext.js +5 -0
- package/lib/module/StaticTreeContext.js.map +1 -0
- package/lib/module/checkSerializable.js +7 -9
- package/lib/module/checkSerializable.js.map +1 -1
- package/lib/module/createNavigationContainerRef.js +6 -8
- package/lib/module/createNavigationContainerRef.js.map +1 -1
- package/lib/module/createNavigatorFactory.js.map +1 -1
- package/lib/module/getActionFromState.js +11 -11
- package/lib/module/getActionFromState.js.map +1 -1
- package/lib/module/getPathFromState.js +38 -39
- package/lib/module/getPathFromState.js.map +1 -1
- package/lib/module/getPatternParts.js +2 -2
- package/lib/module/getPatternParts.js.map +1 -1
- package/lib/module/getStateFromPath.js +72 -45
- package/lib/module/getStateFromPath.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/useEventEmitter.js +1 -1
- package/lib/module/useEventEmitter.js.map +1 -1
- package/lib/module/useKeyedChildListeners.js +4 -2
- package/lib/module/useKeyedChildListeners.js.map +1 -1
- package/lib/module/useNavigation.js +2 -15
- package/lib/module/useNavigation.js.map +1 -1
- package/lib/module/useNavigationBuilder.js +4 -14
- package/lib/module/useNavigationBuilder.js.map +1 -1
- package/lib/module/useNavigationCache.js +19 -14
- package/lib/module/useNavigationCache.js.map +1 -1
- package/lib/module/useNavigationHelpers.js +27 -9
- package/lib/module/useNavigationHelpers.js.map +1 -1
- package/lib/module/useNavigationState.js +69 -36
- package/lib/module/useNavigationState.js.map +1 -1
- package/lib/module/useOnAction.js +21 -2
- package/lib/module/useOnAction.js.map +1 -1
- package/lib/module/useOptionsGetters.js +3 -1
- package/lib/module/useOptionsGetters.js.map +1 -1
- package/lib/module/usePreventRemove.js +9 -2
- package/lib/module/usePreventRemove.js.map +1 -1
- package/lib/module/useRouteCache.js +14 -7
- package/lib/module/useRouteCache.js.map +1 -1
- package/lib/module/useScheduleUpdate.js +8 -8
- package/lib/module/useScheduleUpdate.js.map +1 -1
- package/lib/module/useSyncState.js +4 -1
- package/lib/module/useSyncState.js.map +1 -1
- package/lib/module/validatePathConfig.js.map +1 -1
- package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
- package/lib/typescript/src/DataLoading.d.ts +20 -0
- package/lib/typescript/src/DataLoading.d.ts.map +1 -0
- package/lib/typescript/src/NavigationProvider.d.ts +0 -1
- package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
- package/lib/typescript/src/PreventRemoveContext.d.ts +1 -0
- package/lib/typescript/src/PreventRemoveContext.d.ts.map +1 -1
- package/lib/typescript/src/PreventRemoveProvider.d.ts.map +1 -1
- package/lib/typescript/src/StaticNavigation.d.ts +65 -39
- package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
- package/lib/typescript/src/StaticTreeContext.d.ts +4 -0
- package/lib/typescript/src/StaticTreeContext.d.ts.map +1 -0
- package/lib/typescript/src/checkSerializable.d.ts +4 -2
- package/lib/typescript/src/checkSerializable.d.ts.map +1 -1
- package/lib/typescript/src/createNavigationContainerRef.d.ts.map +1 -1
- package/lib/typescript/src/createNavigatorFactory.d.ts +9 -5
- package/lib/typescript/src/createNavigatorFactory.d.ts.map +1 -1
- package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
- package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
- package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +27 -18
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useKeyedChildListeners.d.ts.map +1 -1
- package/lib/typescript/src/useNavigation.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationBuilder.d.ts +1 -1
- package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationHelpers.d.ts +3 -4
- package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationState.d.ts +11 -1
- package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
- package/lib/typescript/src/useOnAction.d.ts.map +1 -1
- package/lib/typescript/src/useOptionsGetters.d.ts.map +1 -1
- package/lib/typescript/src/usePreventRemove.d.ts.map +1 -1
- package/lib/typescript/src/usePreventRemoveContext.d.ts +1 -0
- package/lib/typescript/src/usePreventRemoveContext.d.ts.map +1 -1
- package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
- package/lib/typescript/src/useScheduleUpdate.d.ts +5 -4
- package/lib/typescript/src/useScheduleUpdate.d.ts.map +1 -1
- package/lib/typescript/src/useSyncState.d.ts.map +1 -1
- package/lib/typescript/src/utilities.d.ts +17 -0
- package/lib/typescript/src/utilities.d.ts.map +1 -1
- package/lib/typescript/src/validatePathConfig.d.ts.map +1 -1
- package/package.json +3 -5
- package/src/BaseNavigationContainer.tsx +9 -2
- package/src/DataLoading.tsx +139 -0
- package/src/NavigationIndependentTree.tsx +11 -11
- package/src/NavigationProvider.tsx +13 -16
- package/src/PreventRemoveContext.tsx +1 -0
- package/src/PreventRemoveProvider.tsx +48 -43
- package/src/StaticNavigation.tsx +157 -93
- package/src/StaticTreeContext.tsx +7 -0
- package/src/checkSerializable.tsx +20 -14
- package/src/createNavigationContainerRef.tsx +12 -9
- package/src/createNavigatorFactory.tsx +16 -28
- package/src/getActionFromState.tsx +18 -16
- package/src/getPathFromState.tsx +69 -65
- package/src/getPatternParts.tsx +2 -2
- package/src/getStateFromPath.tsx +107 -66
- package/src/index.tsx +2 -0
- package/src/types.tsx +74 -55
- package/src/useEventEmitter.tsx +1 -1
- package/src/useKeyedChildListeners.tsx +4 -2
- package/src/useNavigation.tsx +2 -21
- package/src/useNavigationBuilder.tsx +6 -16
- package/src/useNavigationCache.tsx +26 -19
- package/src/useNavigationHelpers.tsx +26 -9
- package/src/useNavigationState.tsx +90 -60
- package/src/useOnAction.tsx +49 -1
- package/src/useOptionsGetters.tsx +4 -1
- package/src/usePreventRemove.tsx +11 -2
- package/src/useRouteCache.tsx +19 -11
- package/src/useScheduleUpdate.tsx +8 -7
- package/src/useSyncState.tsx +5 -1
- package/src/utilities.tsx +28 -0
- package/src/validatePathConfig.tsx +1 -0
package/src/getStateFromPath.tsx
CHANGED
|
@@ -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
|
-
|
|
206
|
+
if (screens === undefined) {
|
|
207
|
+
// When no config is specified, use the path segments as route names
|
|
208
|
+
const routes: ParsedRoute[] = [];
|
|
206
209
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
210
|
+
for (const segment of remaining.split('/')) {
|
|
211
|
+
if (!segment) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
211
214
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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)
|
|
339
|
+
if (!options) {
|
|
340
|
+
return prepareConfigResources();
|
|
341
|
+
}
|
|
299
342
|
|
|
300
343
|
const cached = cachedConfigResources.get(options);
|
|
301
344
|
|
|
302
|
-
if (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
|
-
|
|
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
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
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
|
-
|
|
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
|
|
525
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
728
|
-
children?: never
|
|
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
|
|
736
|
-
children?: never
|
|
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
|
|
747
|
-
getComponent?: never
|
|
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:
|
|
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
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
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
|
|
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
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
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
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
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:
|
|
1486
|
+
Navigator: TypedNavigatorStaticComponent<Bag>;
|
|
1470
1487
|
}>
|
|
1471
1488
|
) => TypedNavigatorStaticDecorated<Bag, Config>;
|
|
1472
|
-
getComponent: () =>
|
|
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
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
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)`.
|
package/src/useEventEmitter.tsx
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
32
|
-
|
|
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]
|
package/src/useNavigation.tsx
CHANGED
|
@@ -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
|
|
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.
|
|
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
|
|
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>
|