@react-navigation/core 7.21.5 → 7.21.6
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 +14 -8
- package/lib/module/BaseNavigationContainer.js.map +1 -1
- package/lib/module/ConsumedParamsContext.js.map +1 -1
- package/lib/module/NavigationIndependentTree.js +8 -4
- package/lib/module/NavigationIndependentTree.js.map +1 -1
- package/lib/module/SceneView.js +16 -39
- package/lib/module/SceneView.js.map +1 -1
- package/lib/module/StaticNavigation.js +1 -1
- package/lib/module/StaticNavigation.js.map +1 -1
- package/lib/module/findFocusedRoute.js +3 -3
- package/lib/module/findFocusedRoute.js.map +1 -1
- package/lib/module/getActionFromState.js.map +1 -1
- package/lib/module/getPathFromState.js +75 -45
- package/lib/module/getPathFromState.js.map +1 -1
- package/lib/module/getPatternParts.js +15 -2
- package/lib/module/getPatternParts.js.map +1 -1
- package/lib/module/getStateFromPath.js +47 -21
- package/lib/module/getStateFromPath.js.map +1 -1
- package/lib/module/getStateFromRouteParams.js +24 -0
- package/lib/module/getStateFromRouteParams.js.map +1 -0
- package/lib/module/useFocusEvents.js +6 -2
- package/lib/module/useFocusEvents.js.map +1 -1
- package/lib/module/useLazyValue.js.map +1 -1
- package/lib/module/useNavigationBuilder.js +8 -6
- package/lib/module/useNavigationBuilder.js.map +1 -1
- package/lib/module/useOnPreventRemove.js +1 -3
- package/lib/module/useOnPreventRemove.js.map +1 -1
- package/lib/module/useRouteCache.js +1 -3
- package/lib/module/useRouteCache.js.map +1 -1
- package/lib/module/useSyncState.js +3 -2
- package/lib/module/useSyncState.js.map +1 -1
- package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
- package/lib/typescript/src/ConsumedParamsContext.d.ts +1 -6
- package/lib/typescript/src/ConsumedParamsContext.d.ts.map +1 -1
- package/lib/typescript/src/EnsureSingleNavigator.d.ts +1 -1
- package/lib/typescript/src/EnsureSingleNavigator.d.ts.map +1 -1
- package/lib/typescript/src/NavigationIndependentTree.d.ts +1 -1
- package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
- package/lib/typescript/src/NavigationProvider.d.ts +1 -1
- package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
- package/lib/typescript/src/PreventRemoveProvider.d.ts +1 -1
- package/lib/typescript/src/PreventRemoveProvider.d.ts.map +1 -1
- package/lib/typescript/src/SceneView.d.ts +2 -1
- package/lib/typescript/src/SceneView.d.ts.map +1 -1
- package/lib/typescript/src/StaticContainer.d.ts +1 -1
- package/lib/typescript/src/StaticContainer.d.ts.map +1 -1
- package/lib/typescript/src/findFocusedRoute.d.ts.map +1 -1
- package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
- package/lib/typescript/src/getPatternParts.d.ts.map +1 -1
- package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
- package/lib/typescript/src/getStateFromRouteParams.d.ts +3 -0
- package/lib/typescript/src/getStateFromRouteParams.d.ts.map +1 -0
- package/lib/typescript/src/theming/ThemeProvider.d.ts +1 -1
- package/lib/typescript/src/theming/ThemeProvider.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +15 -9
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useComponent.d.ts +1 -1
- package/lib/typescript/src/useComponent.d.ts.map +1 -1
- package/lib/typescript/src/useFocusEvents.d.ts.map +1 -1
- package/lib/typescript/src/useLazyValue.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/useNavigationState.d.ts +1 -1
- package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
- package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
- package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
- package/lib/typescript/src/useSyncState.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/BaseNavigationContainer.tsx +16 -10
- package/src/ConsumedParamsContext.tsx +1 -6
- package/src/NavigationIndependentTree.tsx +8 -5
- package/src/SceneView.tsx +18 -51
- package/src/StaticNavigation.tsx +2 -2
- package/src/findFocusedRoute.tsx +5 -3
- package/src/getActionFromState.tsx +3 -1
- package/src/getPathFromState.tsx +130 -59
- package/src/getPatternParts.tsx +15 -2
- package/src/getStateFromPath.tsx +76 -17
- package/src/getStateFromRouteParams.tsx +60 -0
- package/src/types.tsx +42 -24
- package/src/useDescriptors.tsx +1 -1
- package/src/useFocusEvents.tsx +7 -5
- package/src/useLazyValue.tsx +2 -2
- package/src/useNavigationBuilder.tsx +12 -7
- package/src/useOnPreventRemove.tsx +4 -3
- package/src/useRouteCache.tsx +1 -4
- package/src/useSyncState.tsx +3 -2
package/src/getStateFromPath.tsx
CHANGED
|
@@ -26,7 +26,12 @@ type RouteConfig = {
|
|
|
26
26
|
screen: string;
|
|
27
27
|
regex?: RegExp;
|
|
28
28
|
segments: string[];
|
|
29
|
-
params: {
|
|
29
|
+
params: {
|
|
30
|
+
screen: string;
|
|
31
|
+
name: string;
|
|
32
|
+
index: number;
|
|
33
|
+
regex?: RegExp;
|
|
34
|
+
}[];
|
|
30
35
|
routeNames: string[];
|
|
31
36
|
parse?: ParseConfig;
|
|
32
37
|
explicitParamNames?: Set<string>;
|
|
@@ -53,6 +58,7 @@ type ConfigResources = {
|
|
|
53
58
|
initialRoutes: InitialRouteConfig[];
|
|
54
59
|
configs: RouteConfig[];
|
|
55
60
|
configsByScreen: Record<string, RouteConfig[]>;
|
|
61
|
+
prefixRegex?: RegExp;
|
|
56
62
|
};
|
|
57
63
|
|
|
58
64
|
const NESTED_SCREEN_PARAM_NAMES = [
|
|
@@ -64,6 +70,17 @@ const NESTED_SCREEN_PARAM_NAMES = [
|
|
|
64
70
|
'pop',
|
|
65
71
|
];
|
|
66
72
|
|
|
73
|
+
const getStaticSegmentPattern = (segment: string) =>
|
|
74
|
+
Array.from(segment, (char) => {
|
|
75
|
+
const encoded = encodeURIComponent(char);
|
|
76
|
+
const percentEncoded =
|
|
77
|
+
encoded === char
|
|
78
|
+
? `%${char.charCodeAt(0).toString(16).padStart(2, '0').toUpperCase()}`
|
|
79
|
+
: encoded;
|
|
80
|
+
|
|
81
|
+
return `(?:${escape(char)}|${escape(percentEncoded)})`;
|
|
82
|
+
}).join('');
|
|
83
|
+
|
|
67
84
|
const getExplicitParamNames = (parse?: ParseConfig) => {
|
|
68
85
|
const names = Object.entries(parse ?? {}).map(([name]) => name);
|
|
69
86
|
|
|
@@ -95,7 +112,7 @@ export function getStateFromPath<ParamList extends {}>(
|
|
|
95
112
|
path: string,
|
|
96
113
|
options?: Options<ParamList>
|
|
97
114
|
): ResultState | undefined {
|
|
98
|
-
const { initialRoutes, configs, configsByScreen } =
|
|
115
|
+
const { initialRoutes, configs, configsByScreen, prefixRegex } =
|
|
99
116
|
getConfigResources(options);
|
|
100
117
|
|
|
101
118
|
const screens = options?.screens;
|
|
@@ -103,24 +120,20 @@ export function getStateFromPath<ParamList extends {}>(
|
|
|
103
120
|
let remaining = path
|
|
104
121
|
.replace(/\/+/g, '/') // Replace multiple slash (//) with single ones
|
|
105
122
|
.replace(/^\//, '') // Remove extra leading slash
|
|
106
|
-
.replace(/\?.*$/, '')
|
|
123
|
+
.replace(/\?.*$/, '') // Remove query params which we will handle later
|
|
124
|
+
.replace(/%[0-9a-f]{2}/gi, (match) => match.toUpperCase());
|
|
107
125
|
|
|
108
126
|
// Make sure there is a trailing slash
|
|
109
127
|
remaining = remaining.endsWith('/') ? remaining : `${remaining}/`;
|
|
110
128
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (prefix) {
|
|
114
|
-
// Make sure there is a trailing slash
|
|
115
|
-
const normalizedPrefix = prefix.endsWith('/') ? prefix : `${prefix}/`;
|
|
129
|
+
if (prefixRegex) {
|
|
130
|
+
const prefixMatch = remaining.match(prefixRegex);
|
|
116
131
|
|
|
117
|
-
|
|
118
|
-
if (!remaining.startsWith(normalizedPrefix)) {
|
|
132
|
+
if (prefixMatch == null) {
|
|
119
133
|
return undefined;
|
|
120
134
|
}
|
|
121
135
|
|
|
122
|
-
|
|
123
|
-
remaining = remaining.replace(normalizedPrefix, '');
|
|
136
|
+
remaining = remaining.slice(prefixMatch[0].length);
|
|
124
137
|
}
|
|
125
138
|
|
|
126
139
|
if (screens === undefined) {
|
|
@@ -219,6 +232,19 @@ function prepareConfigResources(options?: Options<{}>) {
|
|
|
219
232
|
|
|
220
233
|
const initialRoutes = getInitialRoutes(options);
|
|
221
234
|
const configs = getSortedNormalizedConfigs(initialRoutes, options?.screens);
|
|
235
|
+
const prefix = options?.path?.replace(/^\//, '');
|
|
236
|
+
|
|
237
|
+
let prefixRegex: RegExp | undefined;
|
|
238
|
+
|
|
239
|
+
if (prefix) {
|
|
240
|
+
const normalizedPrefix = prefix.endsWith('/') ? prefix : `${prefix}/`;
|
|
241
|
+
const prefixPattern = normalizedPrefix
|
|
242
|
+
.split('/')
|
|
243
|
+
.map(getStaticSegmentPattern)
|
|
244
|
+
.join('/');
|
|
245
|
+
|
|
246
|
+
prefixRegex = new RegExp(`^${prefixPattern}`);
|
|
247
|
+
}
|
|
222
248
|
|
|
223
249
|
const configsByScreen: Record<string, RouteConfig[]> = {};
|
|
224
250
|
const configsByPattern = new Map<string, RouteConfig>();
|
|
@@ -237,6 +263,7 @@ function prepareConfigResources(options?: Options<{}>) {
|
|
|
237
263
|
initialRoutes,
|
|
238
264
|
configs,
|
|
239
265
|
configsByScreen,
|
|
266
|
+
prefixRegex,
|
|
240
267
|
};
|
|
241
268
|
}
|
|
242
269
|
|
|
@@ -264,6 +291,7 @@ function getSortedNormalizedConfigs(
|
|
|
264
291
|
createNormalizedConfigs(key, screens, initialRoutes, [], [], [])
|
|
265
292
|
)
|
|
266
293
|
)
|
|
294
|
+
.map((config, order) => ({ ...config, order }))
|
|
267
295
|
.sort((a, b) => {
|
|
268
296
|
// Sort config from most specific to least specific:
|
|
269
297
|
// - more segments
|
|
@@ -275,7 +303,21 @@ function getSortedNormalizedConfigs(
|
|
|
275
303
|
// If 2 patterns are same, move the one with less route names up
|
|
276
304
|
// This is an error state, so it's only useful for consistent error messages
|
|
277
305
|
if (isArrayEqual(a.segments, b.segments)) {
|
|
278
|
-
|
|
306
|
+
if (
|
|
307
|
+
a.routeNames.length > b.routeNames.length &&
|
|
308
|
+
arrayStartsWith(a.routeNames, b.routeNames)
|
|
309
|
+
) {
|
|
310
|
+
return -1;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (
|
|
314
|
+
b.routeNames.length > a.routeNames.length &&
|
|
315
|
+
arrayStartsWith(b.routeNames, a.routeNames)
|
|
316
|
+
) {
|
|
317
|
+
return 1;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return a.routeNames.length - b.routeNames.length || a.order - b.order;
|
|
279
321
|
}
|
|
280
322
|
|
|
281
323
|
// If one of the patterns starts with the other, it's more exhaustive
|
|
@@ -441,6 +483,15 @@ const matchAgainstConfigs = (
|
|
|
441
483
|
break;
|
|
442
484
|
}
|
|
443
485
|
|
|
486
|
+
if (
|
|
487
|
+
param.regex &&
|
|
488
|
+
value !== decoded &&
|
|
489
|
+
!param.regex.test(decoded)
|
|
490
|
+
) {
|
|
491
|
+
hasInvalidParam = true;
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
|
|
444
495
|
const parser = routeConfig.parse?.[param.name];
|
|
445
496
|
|
|
446
497
|
paramEntries.push([param.name, parser ? parser(decoded) : decoded]);
|
|
@@ -642,12 +693,18 @@ const createConfigItem = (
|
|
|
642
693
|
`^(${parts
|
|
643
694
|
.map((it, i) => {
|
|
644
695
|
if (it.param) {
|
|
645
|
-
const reg = it.regex
|
|
696
|
+
const reg = it.regex
|
|
697
|
+
? `(?:${it.regex})|(?=[^/]*%[0-9A-F]{2})[^/]+`
|
|
698
|
+
: '[^/]+';
|
|
646
699
|
|
|
647
700
|
return `(((?<param_${i}>${reg})\\/)${it.optional ? '?' : ''})`;
|
|
648
701
|
}
|
|
649
702
|
|
|
650
|
-
|
|
703
|
+
if (it.segment === '*') {
|
|
704
|
+
return `.*\\/`;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
return `${getStaticSegmentPattern(it.segment)}\\/`;
|
|
651
708
|
})
|
|
652
709
|
.join('')})$`
|
|
653
710
|
)
|
|
@@ -666,6 +723,7 @@ const createConfigItem = (
|
|
|
666
723
|
index,
|
|
667
724
|
screen: part.screen,
|
|
668
725
|
name: part.param,
|
|
726
|
+
regex: part.regex ? new RegExp(`^(?:${part.regex})$`) : undefined,
|
|
669
727
|
});
|
|
670
728
|
|
|
671
729
|
if (part.screen === screen) {
|
|
@@ -813,8 +871,9 @@ const parseQueryParams = (
|
|
|
813
871
|
hasNestedScreens = false,
|
|
814
872
|
routeParams?: Record<string, unknown>
|
|
815
873
|
) => {
|
|
816
|
-
const
|
|
817
|
-
const
|
|
874
|
+
const queryIndex = path.indexOf('?');
|
|
875
|
+
const query = queryIndex === -1 ? undefined : path.slice(queryIndex + 1);
|
|
876
|
+
const params: Record<string, unknown> = query ? queryString.parse(query) : {};
|
|
818
877
|
|
|
819
878
|
for (const name of pathParamNames) {
|
|
820
879
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { NavigationState, PartialState } from '@react-navigation/routers';
|
|
2
|
+
|
|
3
|
+
export function getStateFromRouteParams(
|
|
4
|
+
params: object | undefined
|
|
5
|
+
): PartialState<NavigationState> | NavigationState | undefined {
|
|
6
|
+
if (params == null || typeof params !== 'object') {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (
|
|
11
|
+
'state' in params &&
|
|
12
|
+
params.state &&
|
|
13
|
+
typeof params.state === 'object' &&
|
|
14
|
+
'routes' in params.state &&
|
|
15
|
+
Array.isArray(params.state.routes) &&
|
|
16
|
+
params.state.routes.every(
|
|
17
|
+
(route) =>
|
|
18
|
+
typeof route === 'object' &&
|
|
19
|
+
route != null &&
|
|
20
|
+
'name' in route &&
|
|
21
|
+
typeof route.name === 'string'
|
|
22
|
+
)
|
|
23
|
+
) {
|
|
24
|
+
// @ts-expect-error this is fine 🔥
|
|
25
|
+
return params.state;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (
|
|
29
|
+
'screen' in params &&
|
|
30
|
+
params.screen &&
|
|
31
|
+
typeof params.screen === 'string'
|
|
32
|
+
) {
|
|
33
|
+
return {
|
|
34
|
+
routes: [
|
|
35
|
+
{
|
|
36
|
+
name: params.screen,
|
|
37
|
+
params:
|
|
38
|
+
'params' in params &&
|
|
39
|
+
typeof params.params === 'object' &&
|
|
40
|
+
params.params != null
|
|
41
|
+
? params.params
|
|
42
|
+
: undefined,
|
|
43
|
+
path:
|
|
44
|
+
'path' in params && typeof params.path === 'string'
|
|
45
|
+
? params.path
|
|
46
|
+
: undefined,
|
|
47
|
+
// @ts-expect-error this is fine 🔥
|
|
48
|
+
state:
|
|
49
|
+
'params' in params &&
|
|
50
|
+
typeof params.params === 'object' &&
|
|
51
|
+
params.params != null
|
|
52
|
+
? getStateFromRouteParams(params.params)
|
|
53
|
+
: undefined,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
package/src/types.tsx
CHANGED
|
@@ -190,7 +190,7 @@ export type DefaultNavigatorOptions<
|
|
|
190
190
|
ScreenOptions,
|
|
191
191
|
EventMap
|
|
192
192
|
>,
|
|
193
|
-
|
|
193
|
+
ScreenRouteProp<ParamList>
|
|
194
194
|
>
|
|
195
195
|
>;
|
|
196
196
|
children: React.ReactNode;
|
|
@@ -202,7 +202,7 @@ export type DefaultNavigatorOptions<
|
|
|
202
202
|
screenListeners?:
|
|
203
203
|
| ScreenListeners<State, EventMap>
|
|
204
204
|
| ((props: {
|
|
205
|
-
route:
|
|
205
|
+
route: ScreenRouteProp<ParamList>;
|
|
206
206
|
navigation: Navigation;
|
|
207
207
|
}) => ScreenListeners<State, EventMap>);
|
|
208
208
|
|
|
@@ -212,7 +212,7 @@ export type DefaultNavigatorOptions<
|
|
|
212
212
|
screenOptions?:
|
|
213
213
|
| ScreenOptions
|
|
214
214
|
| ((props: {
|
|
215
|
-
route:
|
|
215
|
+
route: ScreenRouteProp<ParamList>;
|
|
216
216
|
navigation: Navigation;
|
|
217
217
|
theme: ReactNavigation.Theme;
|
|
218
218
|
}) => ScreenOptions);
|
|
@@ -647,6 +647,13 @@ export type RouteProp<
|
|
|
647
647
|
RouteName extends keyof ParamList = Keyof<ParamList>,
|
|
648
648
|
> = Route<Extract<RouteName, string>, ParamList[RouteName]>;
|
|
649
649
|
|
|
650
|
+
type ScreenRouteProp<
|
|
651
|
+
ParamList extends ParamListBase,
|
|
652
|
+
RouteName extends keyof ParamList = Keyof<ParamList>,
|
|
653
|
+
> = {
|
|
654
|
+
[Name in Extract<RouteName, string>]: RouteProp<ParamList, Name>;
|
|
655
|
+
}[Extract<RouteName, string>];
|
|
656
|
+
|
|
650
657
|
export type CompositeNavigationProp<
|
|
651
658
|
A extends NavigationProp<ParamListBase, string, any, any, any>,
|
|
652
659
|
B extends NavigationHelpersCommon<ParamListBase, any>,
|
|
@@ -709,7 +716,7 @@ export type ScreenLayoutArgs<
|
|
|
709
716
|
ScreenOptions extends {},
|
|
710
717
|
Navigation,
|
|
711
718
|
> = {
|
|
712
|
-
route:
|
|
719
|
+
route: ScreenRouteProp<ParamList, RouteName>;
|
|
713
720
|
options: ScreenOptions;
|
|
714
721
|
navigation: Navigation;
|
|
715
722
|
theme: ReactNavigation.Theme;
|
|
@@ -896,7 +903,7 @@ export type RouteGroupConfig<
|
|
|
896
903
|
screenOptions?:
|
|
897
904
|
| ScreenOptions
|
|
898
905
|
| ((props: {
|
|
899
|
-
route:
|
|
906
|
+
route: ScreenRouteProp<ParamList>;
|
|
900
907
|
navigation: Navigation;
|
|
901
908
|
theme: ReactNavigation.Theme;
|
|
902
909
|
}) => ScreenOptions);
|
|
@@ -1103,25 +1110,36 @@ export type NavigatorTypeBag<
|
|
|
1103
1110
|
Navigator: Navigator;
|
|
1104
1111
|
};
|
|
1105
1112
|
|
|
1106
|
-
type
|
|
1107
|
-
|
|
1108
|
-
|
|
1113
|
+
type StaticParamListForConfig<Config> =
|
|
1114
|
+
Config extends StaticNavigationConfig['config']
|
|
1115
|
+
? StaticParamList<{ config: Config }> extends infer ParamList extends
|
|
1116
|
+
ParamListBase
|
|
1117
|
+
? ParamList
|
|
1118
|
+
: ParamListBase
|
|
1119
|
+
: ParamListBase;
|
|
1120
|
+
|
|
1121
|
+
type TypedNavigatorStaticComponent<
|
|
1122
|
+
Bag extends NavigatorTypeBagBase,
|
|
1123
|
+
Config,
|
|
1124
|
+
> = React.ComponentType<
|
|
1125
|
+
Partial<
|
|
1126
|
+
Omit<
|
|
1109
1127
|
Omit<
|
|
1110
|
-
React.ComponentProps<
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1128
|
+
React.ComponentProps<Bag['Navigator']>,
|
|
1129
|
+
keyof DefaultNavigatorOptions<any, any, any, any, any, any>
|
|
1130
|
+
> &
|
|
1131
|
+
DefaultNavigatorOptions<
|
|
1132
|
+
StaticParamListForConfig<Config>,
|
|
1133
|
+
Bag['NavigatorID'],
|
|
1134
|
+
Bag['State'],
|
|
1135
|
+
Bag['ScreenOptions'],
|
|
1136
|
+
Bag['EventMap'],
|
|
1137
|
+
Bag['NavigationList'][keyof Bag['ParamList']]
|
|
1120
1138
|
>,
|
|
1121
|
-
|
|
1122
|
-
>
|
|
1139
|
+
'children'
|
|
1123
1140
|
>
|
|
1124
|
-
|
|
1141
|
+
>
|
|
1142
|
+
>;
|
|
1125
1143
|
|
|
1126
1144
|
type TypedNavigatorStaticDecorated<
|
|
1127
1145
|
Bag extends NavigatorTypeBagBase,
|
|
@@ -1154,10 +1172,10 @@ type TypedNavigatorStatic<
|
|
|
1154
1172
|
config: Config;
|
|
1155
1173
|
with: (
|
|
1156
1174
|
Component: React.ComponentType<{
|
|
1157
|
-
Navigator: TypedNavigatorStaticComponent<Bag>;
|
|
1175
|
+
Navigator: TypedNavigatorStaticComponent<Bag, Config>;
|
|
1158
1176
|
}>
|
|
1159
1177
|
) => TypedNavigatorStaticDecorated<Bag, Config>;
|
|
1160
|
-
getComponent: () => TypedNavigatorStaticComponent<Bag>;
|
|
1178
|
+
getComponent: () => TypedNavigatorStaticComponent<Bag, Config>;
|
|
1161
1179
|
} & PrivateValueStore<[Bag['ParamList'], Bag['NavigationList'], unknown]>;
|
|
1162
1180
|
|
|
1163
1181
|
export type TypedNavigator<
|
|
@@ -1284,7 +1302,7 @@ type PathConfigAlias = {
|
|
|
1284
1302
|
export type PathConfig<ParamList extends {}> = Partial<PathConfigAlias> & {
|
|
1285
1303
|
/**
|
|
1286
1304
|
* An object mapping the param name to a function which converts the param value to a string.
|
|
1287
|
-
* By default,
|
|
1305
|
+
* By default, arrays and null are preserved for query params, and other values are converted using `String(value)`.
|
|
1288
1306
|
*
|
|
1289
1307
|
* @example
|
|
1290
1308
|
* ```js
|
package/src/useDescriptors.tsx
CHANGED
package/src/useFocusEvents.tsx
CHANGED
|
@@ -45,7 +45,12 @@ export function useFocusEvents<State extends NavigationState>({
|
|
|
45
45
|
React.useEffect(() => {
|
|
46
46
|
const lastFocusedKey = lastFocusedKeyRef.current;
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
const isFocused = navigation ? navigation.isFocused() : true;
|
|
49
|
+
|
|
50
|
+
if (isFocused) {
|
|
51
|
+
// Remember the current route as focused if the navigator is focused
|
|
52
|
+
lastFocusedKeyRef.current = currentFocusedKey;
|
|
53
|
+
}
|
|
49
54
|
|
|
50
55
|
// We wouldn't have `lastFocusedKey` on initial mount
|
|
51
56
|
// Fire focus event for the current route on mount if there's no parent navigator
|
|
@@ -55,10 +60,7 @@ export function useFocusEvents<State extends NavigationState>({
|
|
|
55
60
|
|
|
56
61
|
// We should only emit events when the focused key changed and navigator is focused
|
|
57
62
|
// When navigator is not focused, screens inside shouldn't receive focused status either
|
|
58
|
-
if (
|
|
59
|
-
lastFocusedKey === currentFocusedKey ||
|
|
60
|
-
!(navigation ? navigation.isFocused() : true)
|
|
61
|
-
) {
|
|
63
|
+
if (lastFocusedKey === currentFocusedKey || !isFocused) {
|
|
62
64
|
return;
|
|
63
65
|
}
|
|
64
66
|
|
package/src/useLazyValue.tsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
export function useLazyValue<T>(create: () => T) {
|
|
3
|
+
export function useLazyValue<T>(create: () => T): T {
|
|
4
4
|
const lazyRef = React.useRef<T>(undefined);
|
|
5
5
|
|
|
6
6
|
if (lazyRef.current === undefined) {
|
|
7
7
|
lazyRef.current = create();
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
return lazyRef.current
|
|
10
|
+
return lazyRef.current;
|
|
11
11
|
}
|
|
@@ -341,7 +341,7 @@ export function useNavigationBuilder<
|
|
|
341
341
|
|
|
342
342
|
const isNestedParamsConsumed =
|
|
343
343
|
typeof route?.params === 'object' && route.params != null
|
|
344
|
-
? consumedParams?.
|
|
344
|
+
? consumedParams?.has(route.params)
|
|
345
345
|
: false;
|
|
346
346
|
|
|
347
347
|
const {
|
|
@@ -575,7 +575,14 @@ export function useNavigationBuilder<
|
|
|
575
575
|
return [stateBeforeInitialization, hydratedState, true, paramsForState];
|
|
576
576
|
}
|
|
577
577
|
|
|
578
|
-
return [
|
|
578
|
+
return [
|
|
579
|
+
undefined,
|
|
580
|
+
hydratedState,
|
|
581
|
+
false,
|
|
582
|
+
// Current state is already initialized
|
|
583
|
+
// So params weren't used, and need to be handled later
|
|
584
|
+
isStateInitialized(currentState) ? undefined : paramsForState,
|
|
585
|
+
];
|
|
579
586
|
}
|
|
580
587
|
// We explicitly don't include routeNames, route.params etc. in the dep list
|
|
581
588
|
// below. We want to avoid forcing a new state to be calculated in those cases
|
|
@@ -723,18 +730,16 @@ export function useNavigationBuilder<
|
|
|
723
730
|
: nextState;
|
|
724
731
|
}
|
|
725
732
|
|
|
726
|
-
const setConsumedParams = consumedParams?.setConsumed;
|
|
727
|
-
|
|
728
733
|
React.useEffect(() => {
|
|
729
734
|
if (
|
|
730
|
-
|
|
735
|
+
consumedParams &&
|
|
731
736
|
didConsumeNestedParams &&
|
|
732
737
|
typeof route?.params === 'object' &&
|
|
733
738
|
route.params != null
|
|
734
739
|
) {
|
|
735
|
-
|
|
740
|
+
consumedParams.set(route.params, true);
|
|
736
741
|
}
|
|
737
|
-
}, [didConsumeNestedParams, route?.params
|
|
742
|
+
}, [consumedParams, didConsumeNestedParams, route?.params]);
|
|
738
743
|
|
|
739
744
|
const shouldUpdate = state !== nextState;
|
|
740
745
|
|
|
@@ -34,9 +34,10 @@ export const shouldPreventRemove = (
|
|
|
34
34
|
.filter((route) => !nextRouteKeys.includes(route.key))
|
|
35
35
|
.reverse();
|
|
36
36
|
|
|
37
|
-
const visitedRouteKeys
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
const visitedRouteKeys =
|
|
38
|
+
VISITED_ROUTE_KEYS in action && action[VISITED_ROUTE_KEYS] instanceof Set
|
|
39
|
+
? action[VISITED_ROUTE_KEYS]
|
|
40
|
+
: new Set<string>();
|
|
40
41
|
|
|
41
42
|
const beforeRemoveAction = {
|
|
42
43
|
...action,
|
package/src/useRouteCache.tsx
CHANGED
|
@@ -41,10 +41,7 @@ export function useRouteCache<State extends NavigationState>(
|
|
|
41
41
|
// FIXME: since the state is updated with mutation, the route object cannot be frozen
|
|
42
42
|
// As a workaround, loop through the object and make the properties readonly
|
|
43
43
|
// Only needed once per proxy - skip if we're reusing a previously-frozen one
|
|
44
|
-
for (const key
|
|
45
|
-
// @ts-expect-error: this is fine since we are looping through the object
|
|
46
|
-
const value = proxy[key];
|
|
47
|
-
|
|
44
|
+
for (const [key, value] of Object.entries(proxy)) {
|
|
48
45
|
Object.defineProperty(proxy, key, {
|
|
49
46
|
enumerable: true,
|
|
50
47
|
configurable: true,
|
package/src/useSyncState.tsx
CHANGED
|
@@ -25,9 +25,10 @@ const createStore = <T,>(getInitialState: () => T) => {
|
|
|
25
25
|
|
|
26
26
|
const setState = (newState: T) => {
|
|
27
27
|
state = deepFreeze(newState);
|
|
28
|
-
didUpdate = true;
|
|
29
28
|
|
|
30
|
-
if (
|
|
29
|
+
if (isBatching) {
|
|
30
|
+
didUpdate = true;
|
|
31
|
+
} else {
|
|
31
32
|
listeners.forEach((listener) => listener());
|
|
32
33
|
}
|
|
33
34
|
};
|