@react-navigation/core 7.21.11 → 7.21.12
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/NavigationBuilderContext.js.map +1 -1
- package/lib/module/useOnPreventRemove.js +35 -24
- package/lib/module/useOnPreventRemove.js.map +1 -1
- package/lib/typescript/src/NavigationBuilderContext.d.ts +2 -2
- package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
- package/lib/typescript/src/useOnPreventRemove.d.ts +2 -6
- package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/NavigationBuilderContext.tsx +5 -1
- package/src/useOnPreventRemove.tsx +68 -36
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","NavigationBuilderContext","createContext","onDispatchAction","undefined","onEmitEvent","onOptionsChange","getIsStateEmitted","scheduleUpdate","Error","flushUpdates"],"sourceRoot":"../../src","sources":["NavigationBuilderContext.tsx"],"mappings":";;
|
|
1
|
+
{"version":3,"names":["React","NavigationBuilderContext","createContext","onDispatchAction","undefined","onEmitEvent","onOptionsChange","getIsStateEmitted","scheduleUpdate","Error","flushUpdates"],"sourceRoot":"../../src","sources":["NavigationBuilderContext.tsx"],"mappings":";;AAMA,OAAO,KAAKA,KAAK,MAAM,OAAO;AAgD9B;AACA;AACA;AACA,OAAO,MAAMC,wBAAwB,gBAAGD,KAAK,CAACE,aAAa,CAiBxD;EACDC,gBAAgB,EAAEA,CAAA,KAAMC,SAAS;EACjCC,WAAW,EAAEA,CAAA,KAAMD,SAAS;EAC5BE,eAAe,EAAEA,CAAA,KAAMF,SAAS;EAChCG,iBAAiB,EAAEA,CAAA,KAAM,KAAK;EAC9BC,cAAc,EAAEA,CAAA,KAAM;IACpB,MAAM,IAAIC,KAAK,CAAC,iDAAiD,CAAC;EACpE,CAAC;EACDC,YAAY,EAAEA,CAAA,KAAM;IAClB,MAAM,IAAID,KAAK,CAAC,+CAA+C,CAAC;EAClE;AACF,CAAC,CAAC","ignoreList":[]}
|
|
@@ -5,37 +5,48 @@ import { NavigationBuilderContext } from "./NavigationBuilderContext.js";
|
|
|
5
5
|
import { NavigationRouteContext } from "./NavigationProvider.js";
|
|
6
6
|
const VISITED_ROUTE_KEYS = Symbol('VISITED_ROUTE_KEYS');
|
|
7
7
|
export const shouldPreventRemove = (emitter, beforeRemoveListeners, currentRoutes, nextRoutes, action) => {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
// Call these in reverse order so last screens handle the event first
|
|
11
|
-
const removedRoutes = currentRoutes.filter(route => !nextRouteKeys.includes(route.key)).reverse();
|
|
8
|
+
const nextRoutesByKey = new Map(nextRoutes.map(route => [route.key, route]).filter(([, route]) => route.key != null));
|
|
12
9
|
const visitedRouteKeys = VISITED_ROUTE_KEYS in action && action[VISITED_ROUTE_KEYS] instanceof Set ? action[VISITED_ROUTE_KEYS] : new Set();
|
|
13
10
|
const beforeRemoveAction = {
|
|
14
11
|
...action,
|
|
15
12
|
[VISITED_ROUTE_KEYS]: visitedRouteKeys
|
|
16
13
|
};
|
|
17
|
-
|
|
14
|
+
|
|
15
|
+
// Call these in reverse order so last screens handle the event first
|
|
16
|
+
const reversedCurrentRoutes = [...currentRoutes].reverse();
|
|
17
|
+
for (const route of reversedCurrentRoutes) {
|
|
18
18
|
if (visitedRouteKeys.has(route.key)) {
|
|
19
19
|
// Skip if we've already emitted this action for this screen
|
|
20
20
|
continue;
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
22
|
+
if (!nextRoutesByKey.has(route.key)) {
|
|
23
|
+
// The route is not in next state, so it's being removed
|
|
24
|
+
// First, we need to check if any child screens want to prevent it
|
|
25
|
+
const isPrevented = beforeRemoveListeners[route.key]?.(beforeRemoveAction, undefined);
|
|
26
|
+
if (isPrevented) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
visitedRouteKeys.add(route.key);
|
|
30
|
+
const event = emitter.emit({
|
|
31
|
+
type: 'beforeRemove',
|
|
32
|
+
target: route.key,
|
|
33
|
+
data: {
|
|
34
|
+
action: beforeRemoveAction
|
|
35
|
+
},
|
|
36
|
+
canPreventDefault: true
|
|
37
|
+
});
|
|
38
|
+
if (event.defaultPrevented) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
// The route is kept but its nested state changed, so propagate the check into the nested navigator
|
|
43
|
+
const nextRoute = nextRoutesByKey.get(route.key);
|
|
44
|
+
if (route.state != null && route.state !== nextRoute?.state) {
|
|
45
|
+
const isPrevented = beforeRemoveListeners[route.key]?.(beforeRemoveAction, nextRoute?.state);
|
|
46
|
+
if (isPrevented) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
39
50
|
}
|
|
40
51
|
}
|
|
41
52
|
return false;
|
|
@@ -52,9 +63,9 @@ export function useOnPreventRemove({
|
|
|
52
63
|
const routeKey = route?.key;
|
|
53
64
|
React.useEffect(() => {
|
|
54
65
|
if (routeKey) {
|
|
55
|
-
return addKeyedListener?.('beforeRemove', routeKey, action => {
|
|
66
|
+
return addKeyedListener?.('beforeRemove', routeKey, (action, nextState) => {
|
|
56
67
|
const state = getState();
|
|
57
|
-
return shouldPreventRemove(emitter, beforeRemoveListeners, state.routes, [], action);
|
|
68
|
+
return shouldPreventRemove(emitter, beforeRemoveListeners, state.routes, nextState?.routes ?? [], action);
|
|
58
69
|
});
|
|
59
70
|
}
|
|
60
71
|
}, [addKeyedListener, beforeRemoveListeners, emitter, getState, routeKey]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","NavigationBuilderContext","NavigationRouteContext","VISITED_ROUTE_KEYS","Symbol","shouldPreventRemove","emitter","beforeRemoveListeners","currentRoutes","nextRoutes","action","
|
|
1
|
+
{"version":3,"names":["React","NavigationBuilderContext","NavigationRouteContext","VISITED_ROUTE_KEYS","Symbol","shouldPreventRemove","emitter","beforeRemoveListeners","currentRoutes","nextRoutes","action","nextRoutesByKey","Map","map","route","key","filter","visitedRouteKeys","Set","beforeRemoveAction","reversedCurrentRoutes","reverse","has","isPrevented","undefined","add","event","emit","type","target","data","canPreventDefault","defaultPrevented","nextRoute","get","state","useOnPreventRemove","getState","addKeyedListener","useContext","routeKey","useEffect","nextState","routes"],"sourceRoot":"../../src","sources":["useOnPreventRemove.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NavigationAction, NavigationState, ParamListBase } from '@react-navigation/routers';
|
|
1
|
+
import type { NavigationAction, NavigationState, ParamListBase, PartialState } from '@react-navigation/routers';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import type { NavigationContainerEventMap, NavigationHelpers } from './types.js';
|
|
4
4
|
export type ListenerMap = {
|
|
@@ -18,7 +18,7 @@ export type FocusedNavigationListener = <T>(callback: FocusedNavigationCallback<
|
|
|
18
18
|
result: T;
|
|
19
19
|
};
|
|
20
20
|
export type GetStateListener = () => NavigationState;
|
|
21
|
-
export type ChildBeforeRemoveListener = (action: NavigationAction) => boolean;
|
|
21
|
+
export type ChildBeforeRemoveListener = (action: NavigationAction, nextState: NavigationState | PartialState<NavigationState> | undefined) => boolean;
|
|
22
22
|
/**
|
|
23
23
|
* Context which holds the required helpers needed to build nested navigators.
|
|
24
24
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NavigationBuilderContext.d.ts","sourceRoot":"","sources":["../../../src/NavigationBuilderContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,aAAa,
|
|
1
|
+
{"version":3,"file":"NavigationBuilderContext.d.ts","sourceRoot":"","sources":["../../../src/NavigationBuilderContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,YAAY,EACb,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,MAAM,YAAS,CAAC;AAE9E,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,yBAAyB,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,YAAY,EAAE,yBAAyB,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,SAAS,MAAM,WAAW,EACpD,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,KACrB,IAAI,CAAC;AAEV,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAC9D,IAAI,EAAE,CAAC,EACP,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAC1B,IAAI,CAAC;AAEV,MAAM,MAAM,mBAAmB,GAAG,CAChC,MAAM,EAAE,gBAAgB,EACxB,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,KAC5B,OAAO,CAAC;AAEb,MAAM,MAAM,yBAAyB,CAAC,CAAC,IAAI,CACzC,UAAU,EAAE,iBAAiB,CAAC,aAAa,CAAC,KACzC,CAAC,CAAC;AAEP,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,EACxC,QAAQ,EAAE,yBAAyB,CAAC,CAAC,CAAC,KACnC;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,CAAC,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,MAAM,eAAe,CAAC;AAErD,MAAM,MAAM,yBAAyB,GAAG,CACtC,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,eAAe,GAAG,YAAY,CAAC,eAAe,CAAC,GAAG,SAAS,KACnE,OAAO,CAAC;AAEb;;GAEG;AACH,eAAO,MAAM,wBAAwB;eACxB,CACT,MAAM,EAAE,gBAAgB,EACxB,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,KAC5B,OAAO;kBACE,WAAW;uBACN,gBAAgB;mBACpB,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI;sBAClB,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI;iBACtD,CACX,KAAK,EAAE,2BAA2B,CAAC,kBAAkB,CAAC,CAAC,MAAM,CAAC,KAC3D,IAAI;qBACQ,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI;uBACvB,MAAM,OAAO;oBAChB,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI;kBAChC,MAAM,IAAI;eACb,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC;EAY9C,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NavigationAction, NavigationState } from '@react-navigation/routers';
|
|
1
|
+
import type { NavigationAction, NavigationRoute, NavigationState, ParamListBase, PartialRoute } from '@react-navigation/routers';
|
|
2
2
|
import { type ChildBeforeRemoveListener } from './NavigationBuilderContext.js';
|
|
3
3
|
import type { EventMapCore } from './types.js';
|
|
4
4
|
import type { NavigationEventEmitter } from './useEventEmitter.js';
|
|
@@ -7,11 +7,7 @@ type Options = {
|
|
|
7
7
|
emitter: NavigationEventEmitter<EventMapCore<any>>;
|
|
8
8
|
beforeRemoveListeners: Record<string, ChildBeforeRemoveListener | undefined>;
|
|
9
9
|
};
|
|
10
|
-
export declare const shouldPreventRemove: (emitter: NavigationEventEmitter<EventMapCore<any>>, beforeRemoveListeners: Record<string, ChildBeforeRemoveListener | undefined>, currentRoutes:
|
|
11
|
-
key: string;
|
|
12
|
-
}[], nextRoutes: {
|
|
13
|
-
key?: string | undefined;
|
|
14
|
-
}[], action: NavigationAction) => boolean;
|
|
10
|
+
export declare const shouldPreventRemove: (emitter: NavigationEventEmitter<EventMapCore<any>>, beforeRemoveListeners: Record<string, ChildBeforeRemoveListener | undefined>, currentRoutes: NavigationRoute<ParamListBase, string>[], nextRoutes: (NavigationRoute<ParamListBase, string> | PartialRoute<NavigationRoute<ParamListBase, string>>)[], action: NavigationAction) => boolean;
|
|
15
11
|
export declare function useOnPreventRemove({ getState, emitter, beforeRemoveListeners, }: Options): void;
|
|
16
12
|
export {};
|
|
17
13
|
//# sourceMappingURL=useOnPreventRemove.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOnPreventRemove.d.ts","sourceRoot":"","sources":["../../../src/useOnPreventRemove.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,
|
|
1
|
+
{"version":3,"file":"useOnPreventRemove.d.ts","sourceRoot":"","sources":["../../../src/useOnPreventRemove.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,aAAa,EACb,YAAY,EACb,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,KAAK,yBAAyB,EAE/B,MAAM,+BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAmB,CAAC;AAEhE,KAAK,OAAO,GAAG;IACb,QAAQ,EAAE,MAAM,eAAe,CAAC;IAChC,OAAO,EAAE,sBAAsB,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;IACnD,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,GAAG,SAAS,CAAC,CAAC;CAC9E,CAAC;AAIF,eAAO,MAAM,mBAAmB,GAC9B,SAAS,sBAAsB,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAClD,uBAAuB,MAAM,CAAC,MAAM,EAAE,yBAAyB,GAAG,SAAS,CAAC,EAC5E,eAAe,eAAe,CAAC,aAAa,EAAE,MAAM,CAAC,EAAE,EACvD,YAAY,CACR,eAAe,CAAC,aAAa,EAAE,MAAM,CAAC,GACtC,YAAY,CAAC,eAAe,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CACvD,EAAE,EACH,QAAQ,gBAAgB,YAqEzB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,OAAO,EACP,qBAAqB,GACtB,EAAE,OAAO,QAwBT"}
|
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": "7.21.
|
|
4
|
+
"version": "7.21.12",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
7
7
|
"react-native",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
]
|
|
84
84
|
]
|
|
85
85
|
},
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "749aaf57b5189363194d63a6b09fe29bacc6740f"
|
|
87
87
|
}
|
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
NavigationAction,
|
|
3
3
|
NavigationState,
|
|
4
4
|
ParamListBase,
|
|
5
|
+
PartialState,
|
|
5
6
|
} from '@react-navigation/routers';
|
|
6
7
|
import * as React from 'react';
|
|
7
8
|
|
|
@@ -46,7 +47,10 @@ export type FocusedNavigationListener = <T>(
|
|
|
46
47
|
|
|
47
48
|
export type GetStateListener = () => NavigationState;
|
|
48
49
|
|
|
49
|
-
export type ChildBeforeRemoveListener = (
|
|
50
|
+
export type ChildBeforeRemoveListener = (
|
|
51
|
+
action: NavigationAction,
|
|
52
|
+
nextState: NavigationState | PartialState<NavigationState> | undefined
|
|
53
|
+
) => boolean;
|
|
50
54
|
|
|
51
55
|
/**
|
|
52
56
|
* Context which holds the required helpers needed to build nested navigators.
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
NavigationAction,
|
|
3
|
+
NavigationRoute,
|
|
3
4
|
NavigationState,
|
|
5
|
+
ParamListBase,
|
|
6
|
+
PartialRoute,
|
|
4
7
|
} from '@react-navigation/routers';
|
|
5
8
|
import * as React from 'react';
|
|
6
9
|
|
|
@@ -23,16 +26,18 @@ const VISITED_ROUTE_KEYS = Symbol('VISITED_ROUTE_KEYS');
|
|
|
23
26
|
export const shouldPreventRemove = (
|
|
24
27
|
emitter: NavigationEventEmitter<EventMapCore<any>>,
|
|
25
28
|
beforeRemoveListeners: Record<string, ChildBeforeRemoveListener | undefined>,
|
|
26
|
-
currentRoutes:
|
|
27
|
-
nextRoutes:
|
|
29
|
+
currentRoutes: NavigationRoute<ParamListBase, string>[],
|
|
30
|
+
nextRoutes: (
|
|
31
|
+
| NavigationRoute<ParamListBase, string>
|
|
32
|
+
| PartialRoute<NavigationRoute<ParamListBase, string>>
|
|
33
|
+
)[],
|
|
28
34
|
action: NavigationAction
|
|
29
35
|
) => {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
.reverse();
|
|
36
|
+
const nextRoutesByKey = new Map(
|
|
37
|
+
nextRoutes
|
|
38
|
+
.map((route) => [route.key, route] as const)
|
|
39
|
+
.filter(([, route]) => route.key != null)
|
|
40
|
+
);
|
|
36
41
|
|
|
37
42
|
const visitedRouteKeys =
|
|
38
43
|
VISITED_ROUTE_KEYS in action && action[VISITED_ROUTE_KEYS] instanceof Set
|
|
@@ -44,30 +49,53 @@ export const shouldPreventRemove = (
|
|
|
44
49
|
[VISITED_ROUTE_KEYS]: visitedRouteKeys,
|
|
45
50
|
};
|
|
46
51
|
|
|
47
|
-
|
|
52
|
+
// Call these in reverse order so last screens handle the event first
|
|
53
|
+
const reversedCurrentRoutes = [...currentRoutes].reverse();
|
|
54
|
+
|
|
55
|
+
for (const route of reversedCurrentRoutes) {
|
|
48
56
|
if (visitedRouteKeys.has(route.key)) {
|
|
49
57
|
// Skip if we've already emitted this action for this screen
|
|
50
58
|
continue;
|
|
51
59
|
}
|
|
52
60
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
if (!nextRoutesByKey.has(route.key)) {
|
|
62
|
+
// The route is not in next state, so it's being removed
|
|
63
|
+
// First, we need to check if any child screens want to prevent it
|
|
64
|
+
const isPrevented = beforeRemoveListeners[route.key]?.(
|
|
65
|
+
beforeRemoveAction,
|
|
66
|
+
undefined
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (isPrevented) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
visitedRouteKeys.add(route.key);
|
|
74
|
+
|
|
75
|
+
const event = emitter.emit({
|
|
76
|
+
type: 'beforeRemove',
|
|
77
|
+
target: route.key,
|
|
78
|
+
data: { action: beforeRemoveAction },
|
|
79
|
+
canPreventDefault: true,
|
|
80
|
+
});
|
|
61
81
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
82
|
+
if (event.defaultPrevented) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
// The route is kept but its nested state changed, so propagate the check into the nested navigator
|
|
87
|
+
const nextRoute = nextRoutesByKey.get(route.key);
|
|
88
|
+
|
|
89
|
+
if (route.state != null && route.state !== nextRoute?.state) {
|
|
90
|
+
const isPrevented = beforeRemoveListeners[route.key]?.(
|
|
91
|
+
beforeRemoveAction,
|
|
92
|
+
nextRoute?.state
|
|
93
|
+
);
|
|
68
94
|
|
|
69
|
-
|
|
70
|
-
|
|
95
|
+
if (isPrevented) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
71
99
|
}
|
|
72
100
|
}
|
|
73
101
|
|
|
@@ -85,17 +113,21 @@ export function useOnPreventRemove({
|
|
|
85
113
|
|
|
86
114
|
React.useEffect(() => {
|
|
87
115
|
if (routeKey) {
|
|
88
|
-
return addKeyedListener?.(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
116
|
+
return addKeyedListener?.(
|
|
117
|
+
'beforeRemove',
|
|
118
|
+
routeKey,
|
|
119
|
+
(action, nextState) => {
|
|
120
|
+
const state = getState();
|
|
121
|
+
|
|
122
|
+
return shouldPreventRemove(
|
|
123
|
+
emitter,
|
|
124
|
+
beforeRemoveListeners,
|
|
125
|
+
state.routes,
|
|
126
|
+
nextState?.routes ?? [],
|
|
127
|
+
action
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
);
|
|
99
131
|
}
|
|
100
132
|
}, [addKeyedListener, beforeRemoveListeners, emitter, getState, routeKey]);
|
|
101
133
|
}
|