@react-navigation/core 7.21.5 → 7.21.7
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 +51 -27
- package/lib/module/useNavigationBuilder.js.map +1 -1
- package/lib/module/useOnAction.js +20 -2
- package/lib/module/useOnAction.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/useOnAction.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 +82 -37
- package/src/useOnAction.tsx +25 -0
- package/src/useOnPreventRemove.tsx +4 -3
- package/src/useRouteCache.tsx +1 -4
- package/src/useSyncState.tsx +3 -2
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
|
}
|
|
@@ -19,7 +19,6 @@ import { ConsumedParamsContext } from './ConsumedParamsContext';
|
|
|
19
19
|
import { deepFreeze } from './deepFreeze';
|
|
20
20
|
import { Group } from './Group';
|
|
21
21
|
import { isArrayEqual } from './isArrayEqual';
|
|
22
|
-
import { isRecordEqual } from './isRecordEqual';
|
|
23
22
|
import { NavigationBuilderContext } from './NavigationBuilderContext';
|
|
24
23
|
import { NavigationHelpersContext } from './NavigationHelpersContext';
|
|
25
24
|
import { NavigationMetaContext } from './NavigationMetaContext';
|
|
@@ -105,7 +104,7 @@ const getRouteConfigsFromChildren = <
|
|
|
105
104
|
EventMap extends EventMapBase,
|
|
106
105
|
>(
|
|
107
106
|
children: React.ReactNode,
|
|
108
|
-
|
|
107
|
+
groupKeys: string[] = [],
|
|
109
108
|
groupOptions?: ScreenConfigWithParent<
|
|
110
109
|
State,
|
|
111
110
|
ScreenOptions,
|
|
@@ -148,7 +147,7 @@ const getRouteConfigsFromChildren = <
|
|
|
148
147
|
}
|
|
149
148
|
|
|
150
149
|
acc.push({
|
|
151
|
-
keys: [
|
|
150
|
+
keys: [...groupKeys, child.props.navigationKey],
|
|
152
151
|
options: groupOptions,
|
|
153
152
|
layout: groupLayout,
|
|
154
153
|
props: child.props as RouteConfig<
|
|
@@ -178,7 +177,9 @@ const getRouteConfigsFromChildren = <
|
|
|
178
177
|
acc.push(
|
|
179
178
|
...getRouteConfigsFromChildren<State, ScreenOptions, EventMap>(
|
|
180
179
|
child.props.children as React.ReactNode,
|
|
181
|
-
child.props.navigationKey
|
|
180
|
+
child.props.navigationKey != null
|
|
181
|
+
? [...groupKeys, child.props.navigationKey]
|
|
182
|
+
: groupKeys,
|
|
182
183
|
// FIXME
|
|
183
184
|
// @ts-expect-error: add validation
|
|
184
185
|
child.type !== Group
|
|
@@ -286,10 +287,23 @@ const getRouteConfigsFromChildren = <
|
|
|
286
287
|
return configs;
|
|
287
288
|
};
|
|
288
289
|
|
|
289
|
-
const getStateFromParams = (
|
|
290
|
+
const getStateFromParams = (
|
|
291
|
+
params: NavigatorRoute['params'],
|
|
292
|
+
type: string
|
|
293
|
+
): NavigationState | PartialState<NavigationState> | undefined => {
|
|
290
294
|
const state = params?.state;
|
|
291
295
|
|
|
292
296
|
if (isNavigationState(state)) {
|
|
297
|
+
if (state.type != null && state.type !== type) {
|
|
298
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
299
|
+
console.error(
|
|
300
|
+
`The state passed in the params is for a navigator of type '${state.type}', but it was passed to a navigator of type '${type}'. This can happen if the state was meant for a different navigator. The state will be discarded.`
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
|
|
293
307
|
return state;
|
|
294
308
|
} else if (typeof params?.screen === 'string' && params?.initial !== false) {
|
|
295
309
|
return {
|
|
@@ -341,7 +355,7 @@ export function useNavigationBuilder<
|
|
|
341
355
|
|
|
342
356
|
const isNestedParamsConsumed =
|
|
343
357
|
typeof route?.params === 'object' && route.params != null
|
|
344
|
-
? consumedParams?.
|
|
358
|
+
? consumedParams?.has(route.params)
|
|
345
359
|
: false;
|
|
346
360
|
|
|
347
361
|
const {
|
|
@@ -399,7 +413,7 @@ export function useNavigationBuilder<
|
|
|
399
413
|
ScreenConfigWithParent<State, ScreenOptions, EventMap>
|
|
400
414
|
> = {};
|
|
401
415
|
|
|
402
|
-
const routeKeyList: Record<string,
|
|
416
|
+
const routeKeyList: Record<string, (string | undefined)[]> = {};
|
|
403
417
|
const routeParamList: Record<string, object | undefined> = {};
|
|
404
418
|
const routeGetIdList: RouterConfigOptions['routeGetIdList'] = {};
|
|
405
419
|
|
|
@@ -413,7 +427,7 @@ export function useNavigationBuilder<
|
|
|
413
427
|
}
|
|
414
428
|
|
|
415
429
|
screens[name] = config;
|
|
416
|
-
routeKeyList[name] = config.keys
|
|
430
|
+
routeKeyList[name] = config.keys;
|
|
417
431
|
routeParamList[name] = config.props.initialParams;
|
|
418
432
|
|
|
419
433
|
Object.assign(routeGetIdList, { [name]: config.props.getId });
|
|
@@ -547,7 +561,7 @@ export function useNavigationBuilder<
|
|
|
547
561
|
} else {
|
|
548
562
|
const paramsForState = isNestedParamsConsumed ? undefined : route?.params;
|
|
549
563
|
const stateFromParams = paramsForState
|
|
550
|
-
? getStateFromParams(paramsForState)
|
|
564
|
+
? getStateFromParams(paramsForState, router.type)
|
|
551
565
|
: undefined;
|
|
552
566
|
|
|
553
567
|
const stateBeforeInitialization = (stateFromParams ?? currentState) as
|
|
@@ -569,13 +583,21 @@ export function useNavigationBuilder<
|
|
|
569
583
|
|
|
570
584
|
if (
|
|
571
585
|
stateBeforeInitialization != null &&
|
|
586
|
+
isStateValid(stateBeforeInitialization) &&
|
|
572
587
|
options.UNSTABLE_routeNamesChangeBehavior === 'lastUnhandled' &&
|
|
573
588
|
doesStateHaveOnlyInvalidRoutes(stateBeforeInitialization)
|
|
574
589
|
) {
|
|
575
590
|
return [stateBeforeInitialization, hydratedState, true, paramsForState];
|
|
576
591
|
}
|
|
577
592
|
|
|
578
|
-
return [
|
|
593
|
+
return [
|
|
594
|
+
undefined,
|
|
595
|
+
hydratedState,
|
|
596
|
+
false,
|
|
597
|
+
// Current state is already initialized
|
|
598
|
+
// So params weren't used, and need to be handled later
|
|
599
|
+
isStateInitialized(currentState) ? undefined : paramsForState,
|
|
600
|
+
];
|
|
579
601
|
}
|
|
580
602
|
// We explicitly don't include routeNames, route.params etc. in the dep list
|
|
581
603
|
// below. We want to avoid forcing a new state to be calculated in those cases
|
|
@@ -593,6 +615,16 @@ export function useNavigationBuilder<
|
|
|
593
615
|
|
|
594
616
|
const previousRouteKeyList = previousRouteKeyListRef.current;
|
|
595
617
|
|
|
618
|
+
// Names of the screens whose navigation key changed since the last render
|
|
619
|
+
const routeKeyChanges = Object.keys(routeKeyList).filter((name) => {
|
|
620
|
+
const current = routeKeyList[name];
|
|
621
|
+
const previous = previousRouteKeyList[name];
|
|
622
|
+
|
|
623
|
+
return (
|
|
624
|
+
current != null && previous != null && !isArrayEqual(current, previous)
|
|
625
|
+
);
|
|
626
|
+
});
|
|
627
|
+
|
|
596
628
|
const [unhandledState, setUnhandledState] = React.useState<
|
|
597
629
|
NavigationState | PartialState<NavigationState> | undefined
|
|
598
630
|
>(stateBeforeInitialization);
|
|
@@ -620,10 +652,14 @@ export function useNavigationBuilder<
|
|
|
620
652
|
let nextState: State = state;
|
|
621
653
|
let shouldClearUnhandledState = false;
|
|
622
654
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
655
|
+
if (unhandledState != null && !isStateValid(unhandledState)) {
|
|
656
|
+
// The stored unhandled state is for a different type of navigator
|
|
657
|
+
// It can't be used with this navigator, so we discard it
|
|
658
|
+
setUnhandledState(undefined);
|
|
659
|
+
} else if (
|
|
660
|
+
// Previously unhandled state is now valid again
|
|
661
|
+
// And current state no longer has any valid routes
|
|
662
|
+
// We should reuse the unhandled state instead of re-calculating the state
|
|
627
663
|
unhandledState?.routes.every((r) => routeNames.includes(r.name)) &&
|
|
628
664
|
state?.routes.every((r) => !routeNames.includes(r.name))
|
|
629
665
|
) {
|
|
@@ -638,18 +674,14 @@ export function useNavigationBuilder<
|
|
|
638
674
|
);
|
|
639
675
|
} else if (
|
|
640
676
|
!isArrayEqual(state.routeNames, routeNames) ||
|
|
641
|
-
|
|
677
|
+
routeKeyChanges.length !== 0
|
|
642
678
|
) {
|
|
643
679
|
// When the list of route names change, the router should handle it to remove invalid routes
|
|
644
680
|
nextState = router.getStateForRouteNamesChange(state, {
|
|
645
681
|
routeNames,
|
|
646
682
|
routeParamList,
|
|
647
683
|
routeGetIdList,
|
|
648
|
-
routeKeyChanges
|
|
649
|
-
(name) =>
|
|
650
|
-
name in previousRouteKeyList &&
|
|
651
|
-
routeKeyList[name] !== previousRouteKeyList[name]
|
|
652
|
-
),
|
|
684
|
+
routeKeyChanges,
|
|
653
685
|
});
|
|
654
686
|
}
|
|
655
687
|
|
|
@@ -657,21 +689,24 @@ export function useNavigationBuilder<
|
|
|
657
689
|
|
|
658
690
|
if (route?.params && !didConsumeNestedParams) {
|
|
659
691
|
let action: CommonActions.Action | undefined;
|
|
660
|
-
const stateFromParams = route.params.state;
|
|
661
692
|
|
|
662
|
-
if (isNavigationState(
|
|
693
|
+
if (isNavigationState(route.params.state) && !isNestedParamsConsumed) {
|
|
663
694
|
didConsumeNestedParams = true;
|
|
664
695
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
696
|
+
const stateFromParams = getStateFromParams(route.params, router.type);
|
|
697
|
+
|
|
698
|
+
if (stateFromParams != null) {
|
|
699
|
+
if (
|
|
700
|
+
options.UNSTABLE_routeNamesChangeBehavior === 'lastUnhandled' &&
|
|
701
|
+
doesStateHaveOnlyInvalidRoutes(stateFromParams)
|
|
702
|
+
) {
|
|
703
|
+
if (stateFromParams !== unhandledState) {
|
|
704
|
+
setUnhandledState(stateFromParams);
|
|
705
|
+
}
|
|
706
|
+
} else {
|
|
707
|
+
// If the route was updated with new state, we should reset to it
|
|
708
|
+
action = CommonActions.reset(stateFromParams);
|
|
671
709
|
}
|
|
672
|
-
} else {
|
|
673
|
-
// If the route was updated with new state, we should reset to it
|
|
674
|
-
action = CommonActions.reset(stateFromParams);
|
|
675
710
|
}
|
|
676
711
|
} else if (
|
|
677
712
|
typeof route.params.screen === 'string' &&
|
|
@@ -684,7 +719,7 @@ export function useNavigationBuilder<
|
|
|
684
719
|
options.UNSTABLE_routeNamesChangeBehavior === 'lastUnhandled' &&
|
|
685
720
|
!routeNames.includes(route.params.screen)
|
|
686
721
|
) {
|
|
687
|
-
const state = getStateFromParams(route.params);
|
|
722
|
+
const state = getStateFromParams(route.params, router.type);
|
|
688
723
|
|
|
689
724
|
if (state != null && !deepEqual(state, unhandledState)) {
|
|
690
725
|
setUnhandledState(state);
|
|
@@ -713,6 +748,18 @@ export function useNavigationBuilder<
|
|
|
713
748
|
})
|
|
714
749
|
: null;
|
|
715
750
|
|
|
751
|
+
if (
|
|
752
|
+
process.env.NODE_ENV !== 'production' &&
|
|
753
|
+
action != null &&
|
|
754
|
+
updatedState === null
|
|
755
|
+
) {
|
|
756
|
+
const names = routeNames.map((name) => `'${name}'`).join(', ');
|
|
757
|
+
|
|
758
|
+
console.error(
|
|
759
|
+
`The 'state' or 'screen' passed in the params couldn't be applied to the navigator with the screens: ${names}. This can happen if the params contain names of screens that don't exist in the navigator. The navigation specified in the params will be ignored.`
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
|
|
716
763
|
nextState =
|
|
717
764
|
updatedState !== null
|
|
718
765
|
? router.getRehydratedState(updatedState, {
|
|
@@ -723,18 +770,16 @@ export function useNavigationBuilder<
|
|
|
723
770
|
: nextState;
|
|
724
771
|
}
|
|
725
772
|
|
|
726
|
-
const setConsumedParams = consumedParams?.setConsumed;
|
|
727
|
-
|
|
728
773
|
React.useEffect(() => {
|
|
729
774
|
if (
|
|
730
|
-
|
|
775
|
+
consumedParams &&
|
|
731
776
|
didConsumeNestedParams &&
|
|
732
777
|
typeof route?.params === 'object' &&
|
|
733
778
|
route.params != null
|
|
734
779
|
) {
|
|
735
|
-
|
|
780
|
+
consumedParams.set(route.params, true);
|
|
736
781
|
}
|
|
737
|
-
}, [didConsumeNestedParams, route?.params
|
|
782
|
+
}, [consumedParams, didConsumeNestedParams, route?.params]);
|
|
738
783
|
|
|
739
784
|
const shouldUpdate = state !== nextState;
|
|
740
785
|
|
package/src/useOnAction.tsx
CHANGED
|
@@ -52,6 +52,7 @@ export function useOnAction<State extends NavigationState>({
|
|
|
52
52
|
onRouteFocus: onRouteFocusParent,
|
|
53
53
|
addListener: addListenerParent,
|
|
54
54
|
onDispatchAction,
|
|
55
|
+
flushUpdates,
|
|
55
56
|
} = React.useContext(NavigationBuilderContext);
|
|
56
57
|
const navigationInChildEnabled = React.useContext(
|
|
57
58
|
DeprecatedNavigationInChildContext
|
|
@@ -69,6 +70,11 @@ export function useOnAction<State extends NavigationState>({
|
|
|
69
70
|
action: NavigationAction,
|
|
70
71
|
visitedNavigators: Set<string> = new Set<string>()
|
|
71
72
|
) => {
|
|
73
|
+
// Apply any pending state updates scheduled during render before handling the action
|
|
74
|
+
// Otherwise the action will be handled with an outdated state,
|
|
75
|
+
// and the pending updates will overwrite the changes from the action
|
|
76
|
+
flushUpdates();
|
|
77
|
+
|
|
72
78
|
const state = getState();
|
|
73
79
|
|
|
74
80
|
// Since actions can bubble both up and down, they could come to the same navigator again
|
|
@@ -91,6 +97,17 @@ export function useOnAction<State extends NavigationState>({
|
|
|
91
97
|
result =
|
|
92
98
|
result === null && action.target === state.key ? state : result;
|
|
93
99
|
|
|
100
|
+
if (result !== null && result.stale !== false) {
|
|
101
|
+
// Some actions (e.g. `RESET`) may return a stale state from the router
|
|
102
|
+
// We rehydrate it before storing so the stored state is always usable
|
|
103
|
+
// Otherwise anything reading the state before the next render
|
|
104
|
+
// (e.g. another dispatch immediately after) would get an outdated state
|
|
105
|
+
result = router.getRehydratedState(
|
|
106
|
+
result,
|
|
107
|
+
routerConfigOptionsRef.current
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
94
111
|
if (result !== null) {
|
|
95
112
|
if (state !== result) {
|
|
96
113
|
const isPrevented = shouldPreventRemove(
|
|
@@ -107,6 +124,13 @@ export function useOnAction<State extends NavigationState>({
|
|
|
107
124
|
return true;
|
|
108
125
|
}
|
|
109
126
|
|
|
127
|
+
if (getState() !== state) {
|
|
128
|
+
// Listeners for 'beforeRemove' may dispatch actions that change the state
|
|
129
|
+
// Then the result we have is outdated and would overwrite those changes
|
|
130
|
+
// So we handle the action again to calculate the result with the latest state
|
|
131
|
+
return onAction(action, new Set<string>());
|
|
132
|
+
}
|
|
133
|
+
|
|
110
134
|
onDispatchAction(action, false);
|
|
111
135
|
|
|
112
136
|
setState(result);
|
|
@@ -158,6 +182,7 @@ export function useOnAction<State extends NavigationState>({
|
|
|
158
182
|
actionListeners,
|
|
159
183
|
beforeRemoveListeners,
|
|
160
184
|
emitter,
|
|
185
|
+
flushUpdates,
|
|
161
186
|
getState,
|
|
162
187
|
navigationInChildEnabled,
|
|
163
188
|
key,
|
|
@@ -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
|
};
|