@react-navigation/core 8.0.0-alpha.36 → 8.0.0-alpha.38
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 +2 -1
- package/lib/module/BaseNavigationContainer.js.map +1 -1
- package/lib/module/NavigationBuilderContext.js.map +1 -1
- package/lib/module/PreventRemoveProvider.js +1 -2
- package/lib/module/PreventRemoveProvider.js.map +1 -1
- package/lib/module/isRecordEqual.js +1 -1
- package/lib/module/isRecordEqual.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/useDescriptors.js +38 -11
- package/lib/module/useDescriptors.js.map +1 -1
- package/lib/module/useNavigationBuilder.js +19 -1
- package/lib/module/useNavigationBuilder.js.map +1 -1
- package/lib/module/useNavigationCache.js +4 -2
- package/lib/module/useNavigationCache.js.map +1 -1
- package/lib/module/useNavigationHelpers.js +3 -9
- package/lib/module/useNavigationHelpers.js.map +1 -1
- package/lib/module/useOnAction.js +17 -2
- package/lib/module/useOnAction.js.map +1 -1
- package/lib/module/useOptionsGetters.js +7 -1
- package/lib/module/useOptionsGetters.js.map +1 -1
- package/lib/module/usePreventRemove.js +1 -2
- package/lib/module/usePreventRemove.js.map +1 -1
- package/lib/module/useRegisterNavigator.js +1 -2
- package/lib/module/useRegisterNavigator.js.map +1 -1
- package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
- package/lib/typescript/src/NavigationBuilderContext.d.ts +1 -0
- package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
- package/lib/typescript/src/PreventRemoveProvider.d.ts.map +1 -1
- package/lib/typescript/src/isRecordEqual.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +2 -2
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useDescriptors.d.ts +3 -2
- package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationBuilder.d.ts +3 -35
- package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationCache.d.ts +3 -2
- package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
- package/lib/typescript/src/useNavigationHelpers.d.ts +5 -36
- package/lib/typescript/src/useNavigationHelpers.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/useRegisterNavigator.d.ts.map +1 -1
- package/package.json +3 -4
- package/src/BaseNavigationContainer.tsx +4 -2
- package/src/NavigationBuilderContext.tsx +1 -0
- package/src/PreventRemoveProvider.tsx +2 -2
- package/src/isRecordEqual.tsx +3 -1
- package/src/types.tsx +3 -2
- package/src/useDescriptors.tsx +100 -23
- package/src/useNavigationBuilder.tsx +32 -1
- package/src/useNavigationCache.tsx +6 -1
- package/src/useNavigationHelpers.tsx +7 -15
- package/src/useOnAction.tsx +27 -2
- package/src/useOptionsGetters.tsx +7 -1
- package/src/usePreventRemove.tsx +1 -2
- package/src/useRegisterNavigator.tsx +2 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-navigation/core",
|
|
3
3
|
"description": "Core utilities for building navigators",
|
|
4
|
-
"version": "8.0.0-alpha.
|
|
4
|
+
"version": "8.0.0-alpha.38",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
7
7
|
"react-native",
|
|
@@ -45,9 +45,8 @@
|
|
|
45
45
|
"clean": "del lib"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@react-navigation/routers": "^8.0.0-alpha.
|
|
48
|
+
"@react-navigation/routers": "^8.0.0-alpha.18",
|
|
49
49
|
"fast-deep-equal": "^3.1.3",
|
|
50
|
-
"nanoid": "^5.1.11",
|
|
51
50
|
"react-is": "^19.2.6",
|
|
52
51
|
"use-latest-callback": "^0.3.5",
|
|
53
52
|
"warn-once": "^0.1.1"
|
|
@@ -88,5 +87,5 @@
|
|
|
88
87
|
]
|
|
89
88
|
]
|
|
90
89
|
},
|
|
91
|
-
"gitHead": "
|
|
90
|
+
"gitHead": "904ba65a2938924d87658b7009a95698f93c7731"
|
|
92
91
|
}
|
|
@@ -513,9 +513,9 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
|
|
|
513
513
|
}, [state, isReady, emitter]);
|
|
514
514
|
|
|
515
515
|
React.useEffect(() => {
|
|
516
|
-
const hydratedState = getRootState();
|
|
517
|
-
|
|
518
516
|
if (process.env.NODE_ENV !== 'production') {
|
|
517
|
+
const hydratedState = getRootState();
|
|
518
|
+
|
|
519
519
|
if (hydratedState !== undefined) {
|
|
520
520
|
const duplicateRouteNamesResult =
|
|
521
521
|
checkDuplicateRouteNames(hydratedState);
|
|
@@ -535,6 +535,8 @@ export function BaseNavigationContainer<ParamList extends {} = RootParamList>({
|
|
|
535
535
|
emitter.emit({ type: 'state', data: { state } });
|
|
536
536
|
|
|
537
537
|
if (!isFirstMountRef.current && onStateChangeRef.current) {
|
|
538
|
+
const hydratedState = getRootState();
|
|
539
|
+
|
|
538
540
|
onStateChangeRef.current(hydratedState);
|
|
539
541
|
}
|
|
540
542
|
|
|
@@ -61,6 +61,7 @@ export type ChildBeforeRemoveListener = (
|
|
|
61
61
|
) => boolean;
|
|
62
62
|
|
|
63
63
|
type NavigationBuilderContextValue = {
|
|
64
|
+
canGoBack?: ((source?: string) => boolean) | undefined;
|
|
64
65
|
onAction?:
|
|
65
66
|
| ((action: NavigationAction, visitedNavigators?: Set<string>) => boolean)
|
|
66
67
|
| undefined;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { nanoid } from 'nanoid/non-secure';
|
|
2
1
|
import * as React from 'react';
|
|
3
2
|
import useLatestCallback from 'use-latest-callback';
|
|
4
3
|
|
|
@@ -34,7 +33,8 @@ const transformPreventedRoutes = (
|
|
|
34
33
|
* Component used for managing which routes have to be prevented from removal in native-stack.
|
|
35
34
|
*/
|
|
36
35
|
export function PreventRemoveProvider({ children }: Props) {
|
|
37
|
-
const
|
|
36
|
+
const parentId = React.useId();
|
|
37
|
+
|
|
38
38
|
const [preventedRoutesMap, setPreventedRoutesMap] =
|
|
39
39
|
React.useState<PreventedRoutesMap>(() => new Map());
|
|
40
40
|
|
package/src/isRecordEqual.tsx
CHANGED
package/src/types.tsx
CHANGED
|
@@ -81,7 +81,7 @@ export type DefaultNavigatorOptions<
|
|
|
81
81
|
layout?:
|
|
82
82
|
| ((props: {
|
|
83
83
|
state: State;
|
|
84
|
-
navigation: NavigationHelpers<ParamList>;
|
|
84
|
+
navigation: NavigationHelpers<ParamList, State>;
|
|
85
85
|
descriptors: Record<
|
|
86
86
|
string,
|
|
87
87
|
Descriptor<
|
|
@@ -470,8 +470,9 @@ type NavigationHelpersRoute<
|
|
|
470
470
|
|
|
471
471
|
export type NavigationHelpers<
|
|
472
472
|
ParamList extends ParamListBase,
|
|
473
|
+
State extends NavigationState = NavigationState<ParamList>,
|
|
473
474
|
EventMap extends EventMapBase = {},
|
|
474
|
-
> = NavigationHelpersCommon<ParamList> &
|
|
475
|
+
> = NavigationHelpersCommon<ParamList, State> &
|
|
475
476
|
EventEmitter<EventMap> &
|
|
476
477
|
NavigationHelpersRoute<ParamList, keyof ParamList> &
|
|
477
478
|
PrivateValueStore<[ParamList, unknown, unknown, unknown]>;
|
package/src/useDescriptors.tsx
CHANGED
|
@@ -8,6 +8,8 @@ import type {
|
|
|
8
8
|
import * as React from 'react';
|
|
9
9
|
import useLatestCallback from 'use-latest-callback';
|
|
10
10
|
|
|
11
|
+
import { isArrayEqual } from './isArrayEqual';
|
|
12
|
+
import { isRecordEqual } from './isRecordEqual';
|
|
11
13
|
import {
|
|
12
14
|
type AddKeyedListener,
|
|
13
15
|
type AddListener,
|
|
@@ -74,10 +76,11 @@ type Options<
|
|
|
74
76
|
ScreenConfigWithParent<State, ScreenOptions, EventMap>
|
|
75
77
|
>;
|
|
76
78
|
state: State;
|
|
77
|
-
navigation: NavigationHelpers<ParamListBase>;
|
|
79
|
+
navigation: NavigationHelpers<ParamListBase, State>;
|
|
78
80
|
screenOptions: ScreenOptionsOrCallback<ScreenOptions> | undefined;
|
|
79
81
|
screenLayout: ScreenLayout<ScreenOptions> | undefined;
|
|
80
82
|
onAction: (action: NavigationAction) => boolean;
|
|
83
|
+
canGoBack: (source?: string) => boolean;
|
|
81
84
|
getState: () => State;
|
|
82
85
|
setState: (state: State) => void;
|
|
83
86
|
subscribe: (callback: () => void) => () => void;
|
|
@@ -88,6 +91,30 @@ type Options<
|
|
|
88
91
|
emitter: NavigationEventEmitter<EventMap>;
|
|
89
92
|
};
|
|
90
93
|
|
|
94
|
+
type DescriptorCache<
|
|
95
|
+
State extends NavigationState,
|
|
96
|
+
ScreenOptions extends {},
|
|
97
|
+
EventMap extends EventMapBase,
|
|
98
|
+
ActionHelpers extends Record<string, (...args: any) => void>,
|
|
99
|
+
> = Record<
|
|
100
|
+
string,
|
|
101
|
+
{
|
|
102
|
+
descriptor: Descriptor<
|
|
103
|
+
ScreenOptions,
|
|
104
|
+
NavigationProp<
|
|
105
|
+
ParamListBase,
|
|
106
|
+
string,
|
|
107
|
+
State,
|
|
108
|
+
ScreenOptions,
|
|
109
|
+
EventMap,
|
|
110
|
+
ActionHelpers
|
|
111
|
+
>,
|
|
112
|
+
RouteProp<ParamListBase>
|
|
113
|
+
>;
|
|
114
|
+
deps: unknown[];
|
|
115
|
+
}
|
|
116
|
+
>;
|
|
117
|
+
|
|
91
118
|
/**
|
|
92
119
|
* Hook to create descriptor objects for the child routes.
|
|
93
120
|
*
|
|
@@ -108,6 +135,7 @@ export function useDescriptors<
|
|
|
108
135
|
screenOptions,
|
|
109
136
|
screenLayout,
|
|
110
137
|
onAction,
|
|
138
|
+
canGoBack,
|
|
111
139
|
getState,
|
|
112
140
|
setState,
|
|
113
141
|
subscribe,
|
|
@@ -136,6 +164,7 @@ export function useDescriptors<
|
|
|
136
164
|
() => ({
|
|
137
165
|
navigation,
|
|
138
166
|
onAction,
|
|
167
|
+
canGoBack,
|
|
139
168
|
addListener,
|
|
140
169
|
addKeyedListener,
|
|
141
170
|
onRouteFocus,
|
|
@@ -150,6 +179,7 @@ export function useDescriptors<
|
|
|
150
179
|
[
|
|
151
180
|
navigation,
|
|
152
181
|
onAction,
|
|
182
|
+
canGoBack,
|
|
153
183
|
addListener,
|
|
154
184
|
addKeyedListener,
|
|
155
185
|
onRouteFocus,
|
|
@@ -170,6 +200,7 @@ export function useDescriptors<
|
|
|
170
200
|
ActionHelpers
|
|
171
201
|
>({
|
|
172
202
|
routes: state.routes,
|
|
203
|
+
canGoBack,
|
|
173
204
|
getState,
|
|
174
205
|
navigation,
|
|
175
206
|
setOptions,
|
|
@@ -183,6 +214,10 @@ export function useDescriptors<
|
|
|
183
214
|
() => state.routes[state.index]?.key
|
|
184
215
|
);
|
|
185
216
|
|
|
217
|
+
const cache = React.useMemo<{
|
|
218
|
+
current: DescriptorCache<State, ScreenOptions, EventMap, ActionHelpers>;
|
|
219
|
+
}>(() => ({ current: {} }), []);
|
|
220
|
+
|
|
186
221
|
const getOptions = (
|
|
187
222
|
route: RouteProp<ParamListBase, string>,
|
|
188
223
|
navigation: NavigationProp<
|
|
@@ -231,7 +266,8 @@ export function useDescriptors<
|
|
|
231
266
|
EventMap
|
|
232
267
|
>,
|
|
233
268
|
customOptions: ScreenOptions,
|
|
234
|
-
routeState: NavigationState | PartialState<NavigationState> | undefined
|
|
269
|
+
routeState: NavigationState | PartialState<NavigationState> | undefined,
|
|
270
|
+
layout: ScreenLayout<ScreenOptions> | undefined
|
|
235
271
|
) => {
|
|
236
272
|
const config = screens[route.name];
|
|
237
273
|
const screen = config?.props;
|
|
@@ -250,14 +286,6 @@ export function useDescriptors<
|
|
|
250
286
|
return o;
|
|
251
287
|
});
|
|
252
288
|
|
|
253
|
-
const layout =
|
|
254
|
-
// The `layout` prop passed to `Screen` elements,
|
|
255
|
-
screen.layout ??
|
|
256
|
-
// The `screenLayout` props passed to `Group` elements
|
|
257
|
-
config?.layout ??
|
|
258
|
-
// The default `screenLayout` passed to the navigator
|
|
259
|
-
screenLayout;
|
|
260
|
-
|
|
261
289
|
let element = (
|
|
262
290
|
<SceneView
|
|
263
291
|
navigation={navigation}
|
|
@@ -293,21 +321,18 @@ export function useDescriptors<
|
|
|
293
321
|
);
|
|
294
322
|
};
|
|
295
323
|
|
|
324
|
+
const next: DescriptorCache<State, ScreenOptions, EventMap, ActionHelpers> =
|
|
325
|
+
{};
|
|
326
|
+
|
|
296
327
|
const descriptors = cachedRoutes.reduce<
|
|
297
328
|
Record<
|
|
298
329
|
string,
|
|
299
|
-
|
|
330
|
+
DescriptorCache<
|
|
331
|
+
State,
|
|
300
332
|
ScreenOptions,
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
State,
|
|
305
|
-
ScreenOptions,
|
|
306
|
-
EventMap,
|
|
307
|
-
ActionHelpers
|
|
308
|
-
>,
|
|
309
|
-
RouteProp<ParamListBase>
|
|
310
|
-
>
|
|
333
|
+
EventMap,
|
|
334
|
+
ActionHelpers
|
|
335
|
+
>[string]['descriptor']
|
|
311
336
|
>
|
|
312
337
|
>((acc, route, i) => {
|
|
313
338
|
const navigation = navigations[route.key];
|
|
@@ -321,14 +346,56 @@ export function useDescriptors<
|
|
|
321
346
|
|
|
322
347
|
const customOptions = getOptions(route, navigation, overrides);
|
|
323
348
|
|
|
349
|
+
const routeState = state.routes[i]?.state;
|
|
350
|
+
const config = screens[route.name];
|
|
351
|
+
const screen = config?.props;
|
|
352
|
+
|
|
353
|
+
const layout =
|
|
354
|
+
// The `layout` prop passed to `Screen` elements,
|
|
355
|
+
screen?.layout ??
|
|
356
|
+
// The `screenLayout` props passed to `Group` elements
|
|
357
|
+
config?.layout ??
|
|
358
|
+
// The default `screenLayout` passed to the navigator
|
|
359
|
+
screenLayout;
|
|
360
|
+
|
|
361
|
+
const deps = [
|
|
362
|
+
route,
|
|
363
|
+
navigation,
|
|
364
|
+
routeState,
|
|
365
|
+
screen?.component,
|
|
366
|
+
screen?.getComponent,
|
|
367
|
+
screen?.children,
|
|
368
|
+
layout,
|
|
369
|
+
context,
|
|
370
|
+
theme,
|
|
371
|
+
getFocusedRouteKey,
|
|
372
|
+
getState,
|
|
373
|
+
setState,
|
|
374
|
+
subscribe,
|
|
375
|
+
];
|
|
376
|
+
|
|
377
|
+
const previous = cache.current[route.key];
|
|
378
|
+
|
|
379
|
+
if (
|
|
380
|
+
previous &&
|
|
381
|
+
isArrayEqual(previous.deps, deps) &&
|
|
382
|
+
isRecordEqual(previous.descriptor.options, customOptions)
|
|
383
|
+
) {
|
|
384
|
+
next[route.key] = previous;
|
|
385
|
+
acc[route.key] = previous.descriptor;
|
|
386
|
+
|
|
387
|
+
return acc;
|
|
388
|
+
}
|
|
389
|
+
|
|
324
390
|
const element = render(
|
|
325
391
|
route,
|
|
326
392
|
navigation,
|
|
327
393
|
customOptions,
|
|
328
|
-
|
|
394
|
+
routeState,
|
|
395
|
+
layout
|
|
329
396
|
);
|
|
330
397
|
|
|
331
|
-
|
|
398
|
+
const descriptor = {
|
|
332
399
|
route,
|
|
333
400
|
navigation,
|
|
334
401
|
render() {
|
|
@@ -337,8 +404,18 @@ export function useDescriptors<
|
|
|
337
404
|
options: customOptions,
|
|
338
405
|
};
|
|
339
406
|
|
|
407
|
+
acc[route.key] = descriptor;
|
|
408
|
+
next[route.key] = {
|
|
409
|
+
descriptor,
|
|
410
|
+
deps,
|
|
411
|
+
};
|
|
412
|
+
|
|
340
413
|
return acc;
|
|
341
414
|
}, {});
|
|
342
415
|
|
|
416
|
+
React.useInsertionEffect(() => {
|
|
417
|
+
cache.current = next;
|
|
418
|
+
});
|
|
419
|
+
|
|
343
420
|
return descriptors;
|
|
344
421
|
}
|
|
@@ -459,7 +459,11 @@ export function useNavigationBuilder<
|
|
|
459
459
|
getIsInitial,
|
|
460
460
|
} = React.use(NavigationStateContext);
|
|
461
461
|
|
|
462
|
-
const {
|
|
462
|
+
const {
|
|
463
|
+
onEmitEvent,
|
|
464
|
+
getIsStateEmitted,
|
|
465
|
+
canGoBack: canGoBackParent,
|
|
466
|
+
} = useNavigationBuilderContext();
|
|
463
467
|
|
|
464
468
|
const mountStateRef = React.useRef<'initial' | 'mounted' | 'unmounted'>(
|
|
465
469
|
'initial'
|
|
@@ -1003,6 +1007,31 @@ export function useNavigationBuilder<
|
|
|
1003
1007
|
onUnhandledActionParent?.(action);
|
|
1004
1008
|
});
|
|
1005
1009
|
|
|
1010
|
+
const canGoBack = useLatestCallback((source?: string) => {
|
|
1011
|
+
const state = getState();
|
|
1012
|
+
|
|
1013
|
+
return (
|
|
1014
|
+
router.getStateForAction(
|
|
1015
|
+
state,
|
|
1016
|
+
{ ...CommonActions.goBack(), source },
|
|
1017
|
+
{
|
|
1018
|
+
routeNames: state.routeNames,
|
|
1019
|
+
routeParamList: {},
|
|
1020
|
+
routeGetIdList: {},
|
|
1021
|
+
}
|
|
1022
|
+
) !== null ||
|
|
1023
|
+
canGoBackParent?.(
|
|
1024
|
+
// If the `source` belonged to the current navigator,
|
|
1025
|
+
// rewrite the `source` to the parent screen's key when checking
|
|
1026
|
+
// This mirrors the way actions bubble up to the parent
|
|
1027
|
+
source !== undefined && state.routes.some((r) => r.key === source)
|
|
1028
|
+
? route?.key
|
|
1029
|
+
: source
|
|
1030
|
+
) ||
|
|
1031
|
+
false
|
|
1032
|
+
);
|
|
1033
|
+
});
|
|
1034
|
+
|
|
1006
1035
|
const navigation = useNavigationHelpers<
|
|
1007
1036
|
State,
|
|
1008
1037
|
ActionHelpers,
|
|
@@ -1011,6 +1040,7 @@ export function useNavigationBuilder<
|
|
|
1011
1040
|
>({
|
|
1012
1041
|
onAction,
|
|
1013
1042
|
onUnhandledAction,
|
|
1043
|
+
canGoBack,
|
|
1014
1044
|
getState,
|
|
1015
1045
|
emitter,
|
|
1016
1046
|
router,
|
|
@@ -1040,6 +1070,7 @@ export function useNavigationBuilder<
|
|
|
1040
1070
|
screenOptions,
|
|
1041
1071
|
screenLayout,
|
|
1042
1072
|
onAction,
|
|
1073
|
+
canGoBack,
|
|
1043
1074
|
getState,
|
|
1044
1075
|
setState,
|
|
1045
1076
|
subscribe,
|
|
@@ -19,8 +19,9 @@ type Options<
|
|
|
19
19
|
> = {
|
|
20
20
|
routes: State['routes'];
|
|
21
21
|
getState: () => State;
|
|
22
|
-
navigation: NavigationHelpers<ParamListBase> &
|
|
22
|
+
navigation: NavigationHelpers<ParamListBase, State> &
|
|
23
23
|
Partial<NavigationProp<ParamListBase, string, any, any, any>>;
|
|
24
|
+
canGoBack: (source?: string) => boolean;
|
|
24
25
|
setOptions: (
|
|
25
26
|
cb: (
|
|
26
27
|
options: Record<string, Partial<ScreenOptions> | undefined>
|
|
@@ -68,6 +69,7 @@ export function useNavigationCache<
|
|
|
68
69
|
routes,
|
|
69
70
|
getState,
|
|
70
71
|
navigation,
|
|
72
|
+
canGoBack,
|
|
71
73
|
setOptions,
|
|
72
74
|
router,
|
|
73
75
|
emitter,
|
|
@@ -89,6 +91,7 @@ export function useNavigationCache<
|
|
|
89
91
|
getState,
|
|
90
92
|
parentNavigation,
|
|
91
93
|
navigation,
|
|
94
|
+
canGoBack,
|
|
92
95
|
setOptions,
|
|
93
96
|
router,
|
|
94
97
|
emitter,
|
|
@@ -155,6 +158,7 @@ export function useNavigationCache<
|
|
|
155
158
|
// FIXME: too much work to fix the types for now
|
|
156
159
|
...(emitter.create(route.key) as any),
|
|
157
160
|
dispatch,
|
|
161
|
+
canGoBack: () => canGoBack(route.key),
|
|
158
162
|
getParent: (routeName) => {
|
|
159
163
|
if (routeName === route.name) {
|
|
160
164
|
// If the passed route name is the same as the current route's name,
|
|
@@ -203,6 +207,7 @@ export function useNavigationCache<
|
|
|
203
207
|
routes,
|
|
204
208
|
getState,
|
|
205
209
|
navigation,
|
|
210
|
+
canGoBack,
|
|
206
211
|
router,
|
|
207
212
|
withStackTrace,
|
|
208
213
|
emitter,
|
|
@@ -19,6 +19,7 @@ PrivateValueStore;
|
|
|
19
19
|
type Options<State extends NavigationState, Action extends NavigationAction> = {
|
|
20
20
|
onAction: (action: NavigationAction) => boolean;
|
|
21
21
|
onUnhandledAction: (action: NavigationAction) => void;
|
|
22
|
+
canGoBack: (source?: string) => boolean;
|
|
22
23
|
getState: () => State;
|
|
23
24
|
emitter: NavigationEventEmitter<any>;
|
|
24
25
|
router: Router<State, Action>;
|
|
@@ -36,11 +37,13 @@ export function useNavigationHelpers<
|
|
|
36
37
|
>({
|
|
37
38
|
onAction,
|
|
38
39
|
onUnhandledAction,
|
|
40
|
+
canGoBack,
|
|
39
41
|
getState,
|
|
40
42
|
emitter,
|
|
41
43
|
router,
|
|
42
44
|
}: Options<State, Action>) {
|
|
43
45
|
const parentNavigationHelpers = React.use(NavigationContext);
|
|
46
|
+
|
|
44
47
|
const { withStackTrace } = useNavigationBuilderContext();
|
|
45
48
|
|
|
46
49
|
return React.useMemo(() => {
|
|
@@ -82,27 +85,16 @@ export function useNavigationHelpers<
|
|
|
82
85
|
isFocused: parentNavigationHelpers
|
|
83
86
|
? parentNavigationHelpers.isFocused
|
|
84
87
|
: () => true,
|
|
85
|
-
canGoBack: () =>
|
|
86
|
-
const state = getState();
|
|
87
|
-
|
|
88
|
-
return (
|
|
89
|
-
router.getStateForAction(state, CommonActions.goBack() as Action, {
|
|
90
|
-
routeNames: state.routeNames,
|
|
91
|
-
routeParamList: {},
|
|
92
|
-
routeGetIdList: {},
|
|
93
|
-
}) !== null ||
|
|
94
|
-
parentNavigationHelpers?.canGoBack() ||
|
|
95
|
-
false
|
|
96
|
-
);
|
|
97
|
-
},
|
|
88
|
+
canGoBack: () => canGoBack(),
|
|
98
89
|
getState,
|
|
99
|
-
} as NavigationHelpers<ParamListBase, EventMap> & ActionHelpers;
|
|
90
|
+
} as NavigationHelpers<ParamListBase, State, EventMap> & ActionHelpers;
|
|
100
91
|
|
|
101
92
|
return navigationHelpers;
|
|
102
93
|
}, [
|
|
103
|
-
router,
|
|
94
|
+
router.actionCreators,
|
|
104
95
|
parentNavigationHelpers,
|
|
105
96
|
emitter.emit,
|
|
97
|
+
canGoBack,
|
|
106
98
|
getState,
|
|
107
99
|
onAction,
|
|
108
100
|
onUnhandledAction,
|
package/src/useOnAction.tsx
CHANGED
|
@@ -166,19 +166,44 @@ export function useOnAction<State extends NavigationState>({
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
const isSourceInCurrentNavigator =
|
|
170
|
+
action.source !== undefined &&
|
|
171
|
+
state.routes.some((r) => r.key === action.source);
|
|
172
|
+
|
|
169
173
|
if (onActionParent !== undefined) {
|
|
170
174
|
// Bubble action to the parent if the current navigator didn't handle it
|
|
171
|
-
|
|
175
|
+
const parentAction =
|
|
176
|
+
isSourceInCurrentNavigator && key !== undefined
|
|
177
|
+
? {
|
|
178
|
+
...action,
|
|
179
|
+
// If the `source` belonged to the current navigator,
|
|
180
|
+
// rewrite the `source` to the parent screen's key when bubbling
|
|
181
|
+
// This will let parent handle it relative to the child that dispatched the action
|
|
182
|
+
source: key,
|
|
183
|
+
}
|
|
184
|
+
: action;
|
|
185
|
+
|
|
186
|
+
if (onActionParent(parentAction, visitedNavigators)) {
|
|
172
187
|
return true;
|
|
173
188
|
}
|
|
174
189
|
}
|
|
175
190
|
|
|
176
191
|
if (typeof action.target === 'string') {
|
|
177
192
|
// If the action wasn't handled by current navigator or a parent navigator, let children handle it
|
|
193
|
+
const childAction = isSourceInCurrentNavigator
|
|
194
|
+
? {
|
|
195
|
+
...action,
|
|
196
|
+
// If the `source` belonged to the current navigator,
|
|
197
|
+
// remove it when targeting a child navigator
|
|
198
|
+
// So child navigators can handle it based on the focused screen
|
|
199
|
+
source: undefined,
|
|
200
|
+
}
|
|
201
|
+
: action;
|
|
202
|
+
|
|
178
203
|
for (let i = actionListeners.length - 1; i >= 0; i--) {
|
|
179
204
|
const listener = actionListeners[i];
|
|
180
205
|
|
|
181
|
-
if (listener?.(
|
|
206
|
+
if (listener?.(childAction, visitedNavigators)) {
|
|
182
207
|
return true;
|
|
183
208
|
}
|
|
184
209
|
}
|
|
@@ -80,6 +80,12 @@ export function useOptionsGetters({
|
|
|
80
80
|
(key: string, getter: () => object | undefined | null) => {
|
|
81
81
|
optionsGettersFromChildRef.current[key] = getter;
|
|
82
82
|
|
|
83
|
+
// Hidden screens may register without running their passive effects.
|
|
84
|
+
// Wait until the commit finishes before reporting their options.
|
|
85
|
+
if (onOptionsChange !== undefined) {
|
|
86
|
+
void Promise.resolve().then(onOptionsChange);
|
|
87
|
+
}
|
|
88
|
+
|
|
83
89
|
return () => {
|
|
84
90
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
85
91
|
delete optionsGettersFromChildRef.current[key];
|
|
@@ -87,7 +93,7 @@ export function useOptionsGetters({
|
|
|
87
93
|
// Other getters and the focused route may still change during cleanup.
|
|
88
94
|
// Wait until the commit finishes before reporting the remaining options.
|
|
89
95
|
if (onOptionsChange !== undefined) {
|
|
90
|
-
|
|
96
|
+
void Promise.resolve().then(onOptionsChange);
|
|
91
97
|
}
|
|
92
98
|
};
|
|
93
99
|
},
|
package/src/usePreventRemove.tsx
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { NavigationAction } from '@react-navigation/routers';
|
|
2
|
-
import { nanoid } from 'nanoid/non-secure';
|
|
3
2
|
import * as React from 'react';
|
|
4
3
|
import useLatestCallback from 'use-latest-callback';
|
|
5
4
|
|
|
@@ -18,7 +17,7 @@ export function usePreventRemove(
|
|
|
18
17
|
preventRemove: boolean,
|
|
19
18
|
callback: (options: { data: { action: NavigationAction } }) => void
|
|
20
19
|
) {
|
|
21
|
-
const
|
|
20
|
+
const id = React.useId();
|
|
22
21
|
|
|
23
22
|
const navigation = useNavigation();
|
|
24
23
|
const { key: routeKey } = useRoute();
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { nanoid } from 'nanoid/non-secure';
|
|
2
1
|
import * as React from 'react';
|
|
3
2
|
|
|
4
3
|
import { SingleNavigatorContext } from './EnsureSingleNavigator';
|
|
@@ -8,7 +7,8 @@ import { SingleNavigatorContext } from './EnsureSingleNavigator';
|
|
|
8
7
|
* This is used to prevent multiple navigators under a single container or screen.
|
|
9
8
|
*/
|
|
10
9
|
export function useRegisterNavigator() {
|
|
11
|
-
const
|
|
10
|
+
const key = React.useId();
|
|
11
|
+
|
|
12
12
|
const container = React.use(SingleNavigatorContext);
|
|
13
13
|
|
|
14
14
|
if (container === undefined) {
|