@react-navigation/core 6.2.0 → 6.3.0
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/README.md +17 -11
- package/lib/commonjs/PreventRemoveContext.js +17 -0
- package/lib/commonjs/PreventRemoveContext.js.map +1 -0
- package/lib/commonjs/PreventRemoveProvider.js +114 -0
- package/lib/commonjs/PreventRemoveProvider.js.map +1 -0
- package/lib/commonjs/getStateFromPath.js +1 -1
- package/lib/commonjs/getStateFromPath.js.map +1 -1
- package/lib/commonjs/index.js +36 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/useChildListeners.js +6 -3
- package/lib/commonjs/useChildListeners.js.map +1 -1
- package/lib/commonjs/useComponent.js +22 -12
- package/lib/commonjs/useComponent.js.map +1 -1
- package/lib/commonjs/useEventEmitter.js +13 -3
- package/lib/commonjs/useEventEmitter.js.map +1 -1
- package/lib/commonjs/useFocusEffect.js +1 -1
- package/lib/commonjs/useFocusEffect.js.map +1 -1
- package/lib/commonjs/useKeyedChildListeners.js +4 -2
- package/lib/commonjs/useKeyedChildListeners.js.map +1 -1
- package/lib/commonjs/useNavigationBuilder.js +4 -2
- package/lib/commonjs/useNavigationBuilder.js.map +1 -1
- package/lib/commonjs/useNavigationHelpers.js +0 -4
- package/lib/commonjs/useNavigationHelpers.js.map +1 -1
- package/lib/commonjs/usePreventRemove.js +59 -0
- package/lib/commonjs/usePreventRemove.js.map +1 -0
- package/lib/commonjs/usePreventRemoveContext.js +27 -0
- package/lib/commonjs/usePreventRemoveContext.js.map +1 -0
- package/lib/module/PreventRemoveContext.js +9 -0
- package/lib/module/PreventRemoveContext.js.map +1 -0
- package/lib/module/PreventRemoveProvider.js +96 -0
- package/lib/module/PreventRemoveProvider.js.map +1 -0
- package/lib/module/getStateFromPath.js +1 -1
- package/lib/module/getStateFromPath.js.map +1 -1
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/useChildListeners.js +6 -3
- package/lib/module/useChildListeners.js.map +1 -1
- package/lib/module/useComponent.js +23 -12
- package/lib/module/useComponent.js.map +1 -1
- package/lib/module/useEventEmitter.js +13 -3
- package/lib/module/useEventEmitter.js.map +1 -1
- package/lib/module/useFocusEffect.js +1 -1
- package/lib/module/useFocusEffect.js.map +1 -1
- package/lib/module/useKeyedChildListeners.js +4 -2
- package/lib/module/useKeyedChildListeners.js.map +1 -1
- package/lib/module/useNavigationBuilder.js +3 -2
- package/lib/module/useNavigationBuilder.js.map +1 -1
- package/lib/module/useNavigationHelpers.js +0 -4
- package/lib/module/useNavigationHelpers.js.map +1 -1
- package/lib/module/usePreventRemove.js +41 -0
- package/lib/module/usePreventRemove.js.map +1 -0
- package/lib/module/usePreventRemoveContext.js +12 -0
- package/lib/module/usePreventRemoveContext.js.map +1 -0
- package/lib/typescript/src/NavigationBuilderContext.d.ts +1 -1
- package/lib/typescript/src/PreventRemoveContext.d.ts +13 -0
- package/lib/typescript/src/PreventRemoveProvider.d.ts +9 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/types.d.ts +10 -6
- package/lib/typescript/src/useComponent.d.ts +5 -1
- package/lib/typescript/src/useDescriptors.d.ts +3 -3
- package/lib/typescript/src/useNavigationBuilder.d.ts +9 -7
- package/lib/typescript/src/useNavigationHelpers.d.ts +3 -3
- package/lib/typescript/src/usePreventRemove.d.ts +12 -0
- package/lib/typescript/src/usePreventRemoveContext.d.ts +4 -0
- package/package.json +10 -9
- package/src/PreventRemoveContext.tsx +21 -0
- package/src/PreventRemoveProvider.tsx +126 -0
- package/src/getStateFromPath.tsx +4 -1
- package/src/index.tsx +4 -0
- package/src/types.tsx +39 -14
- package/src/useChildListeners.tsx +5 -3
- package/src/useComponent.tsx +19 -12
- package/src/useEventEmitter.tsx +14 -3
- package/src/useFocusEffect.tsx +1 -1
- package/src/useKeyedChildListeners.tsx +8 -4
- package/src/useNavigationBuilder.tsx +6 -3
- package/src/useNavigationHelpers.tsx +0 -6
- package/src/usePreventRemove.tsx +51 -0
- package/src/usePreventRemoveContext.tsx +15 -0
|
@@ -50,8 +50,8 @@ export default function useDescriptors<State extends NavigationState, ActionHelp
|
|
|
50
50
|
source?: string | undefined;
|
|
51
51
|
target?: string | undefined;
|
|
52
52
|
}>)): void;
|
|
53
|
-
navigate<RouteName extends string>(...args: [screen: RouteName] | [screen: RouteName, params: object | undefined]): void;
|
|
54
|
-
navigate<RouteName_1 extends string>(options: {
|
|
53
|
+
navigate<RouteName extends string>(...args: RouteName extends unknown ? [screen: RouteName] | [screen: RouteName, params: object | undefined] : never): void;
|
|
54
|
+
navigate<RouteName_1 extends string>(options: RouteName_1 extends unknown ? {
|
|
55
55
|
key: string;
|
|
56
56
|
params?: object | undefined;
|
|
57
57
|
merge?: boolean | undefined;
|
|
@@ -60,7 +60,7 @@ export default function useDescriptors<State extends NavigationState, ActionHelp
|
|
|
60
60
|
key?: string | undefined;
|
|
61
61
|
params: object | undefined;
|
|
62
62
|
merge?: boolean | undefined;
|
|
63
|
-
}): void;
|
|
63
|
+
} : never): void;
|
|
64
64
|
reset(state: State | import("@react-navigation/routers").PartialState<State>): void;
|
|
65
65
|
goBack(): void;
|
|
66
66
|
isFocused(): boolean;
|
|
@@ -38,8 +38,8 @@ export default function useNavigationBuilder<State extends NavigationState, Rout
|
|
|
38
38
|
source?: string | undefined;
|
|
39
39
|
target?: string | undefined;
|
|
40
40
|
}>)): void;
|
|
41
|
-
navigate<RouteName extends string>(...args: [screen: RouteName] | [screen: RouteName, params: object | undefined]): void;
|
|
42
|
-
navigate<RouteName_1 extends string>(options: {
|
|
41
|
+
navigate<RouteName extends string>(...args: RouteName extends unknown ? [screen: RouteName] | [screen: RouteName, params: object | undefined] : never): void;
|
|
42
|
+
navigate<RouteName_1 extends string>(options: RouteName_1 extends unknown ? {
|
|
43
43
|
key: string;
|
|
44
44
|
params?: object | undefined;
|
|
45
45
|
merge?: boolean | undefined;
|
|
@@ -48,7 +48,7 @@ export default function useNavigationBuilder<State extends NavigationState, Rout
|
|
|
48
48
|
key?: string | undefined;
|
|
49
49
|
params: object | undefined;
|
|
50
50
|
merge?: boolean | undefined;
|
|
51
|
-
}): void;
|
|
51
|
+
} : never): void;
|
|
52
52
|
reset(state: Readonly<{
|
|
53
53
|
key: string;
|
|
54
54
|
index: number;
|
|
@@ -119,8 +119,8 @@ export default function useNavigationBuilder<State extends NavigationState, Rout
|
|
|
119
119
|
source?: string | undefined;
|
|
120
120
|
target?: string | undefined;
|
|
121
121
|
}>)): void;
|
|
122
|
-
navigate<RouteName extends string>(...args: [screen: RouteName] | [screen: RouteName, params: object | undefined]): void;
|
|
123
|
-
navigate<RouteName_1 extends string>(options: {
|
|
122
|
+
navigate<RouteName extends string>(...args: RouteName extends unknown ? [screen: RouteName] | [screen: RouteName, params: object | undefined] : never): void;
|
|
123
|
+
navigate<RouteName_1 extends string>(options: RouteName_1 extends unknown ? {
|
|
124
124
|
key: string;
|
|
125
125
|
params?: object | undefined;
|
|
126
126
|
merge?: boolean | undefined;
|
|
@@ -129,7 +129,7 @@ export default function useNavigationBuilder<State extends NavigationState, Rout
|
|
|
129
129
|
key?: string | undefined;
|
|
130
130
|
params: object | undefined;
|
|
131
131
|
merge?: boolean | undefined;
|
|
132
|
-
}): void;
|
|
132
|
+
} : never): void;
|
|
133
133
|
reset(state: State | PartialState<State>): void;
|
|
134
134
|
goBack(): void;
|
|
135
135
|
isFocused(): boolean;
|
|
@@ -158,5 +158,7 @@ export default function useNavigationBuilder<State extends NavigationState, Rout
|
|
|
158
158
|
setParams(params: Partial<object | undefined>): void;
|
|
159
159
|
setOptions(options: Partial<ScreenOptions>): void;
|
|
160
160
|
} & import("./types").EventConsumer<EventMap & EventMapCore<State>> & PrivateValueStore<[ParamListBase, string, EventMap]> & ActionHelpers, import("./types").RouteProp<ParamListBase, string>>>;
|
|
161
|
-
NavigationContent: (
|
|
161
|
+
NavigationContent: ({ children }: {
|
|
162
|
+
children: React.ReactNode;
|
|
163
|
+
}) => JSX.Element;
|
|
162
164
|
};
|
|
@@ -40,8 +40,8 @@ export default function useNavigationHelpers<State extends NavigationState, Acti
|
|
|
40
40
|
source?: string | undefined;
|
|
41
41
|
target?: string | undefined;
|
|
42
42
|
}>)): void;
|
|
43
|
-
navigate<RouteName extends string>(...args: [screen: RouteName] | [screen: RouteName, params: object | undefined]): void;
|
|
44
|
-
navigate<RouteName_1 extends string>(options: {
|
|
43
|
+
navigate<RouteName extends string>(...args: RouteName extends unknown ? [screen: RouteName] | [screen: RouteName, params: object | undefined] : never): void;
|
|
44
|
+
navigate<RouteName_1 extends string>(options: RouteName_1 extends unknown ? {
|
|
45
45
|
key: string;
|
|
46
46
|
params?: object | undefined;
|
|
47
47
|
merge?: boolean | undefined;
|
|
@@ -50,7 +50,7 @@ export default function useNavigationHelpers<State extends NavigationState, Acti
|
|
|
50
50
|
key?: string | undefined;
|
|
51
51
|
params: object | undefined;
|
|
52
52
|
merge?: boolean | undefined;
|
|
53
|
-
}): void;
|
|
53
|
+
} : never): void;
|
|
54
54
|
reset(state: Readonly<{
|
|
55
55
|
key: string;
|
|
56
56
|
index: number;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NavigationAction } from '@react-navigation/routers';
|
|
2
|
+
/**
|
|
3
|
+
* Hook to prevent screen from being removed. Can be used to prevent users from leaving the screen.
|
|
4
|
+
*
|
|
5
|
+
* @param preventRemove Boolean indicating whether to prevent screen from being removed.
|
|
6
|
+
* @param callback Function which is executed when screen was prevented from being removed.
|
|
7
|
+
*/
|
|
8
|
+
export default function usePreventRemove(preventRemove: boolean, callback: (options: {
|
|
9
|
+
data: {
|
|
10
|
+
action: NavigationAction;
|
|
11
|
+
};
|
|
12
|
+
}) => void): void;
|
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": "6.
|
|
4
|
+
"version": "6.3.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
7
7
|
"react-native",
|
|
@@ -31,26 +31,27 @@
|
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
|
-
"
|
|
34
|
+
"prepack": "bob build",
|
|
35
35
|
"clean": "del lib"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@react-navigation/routers": "^6.1.
|
|
38
|
+
"@react-navigation/routers": "^6.1.2",
|
|
39
39
|
"escape-string-regexp": "^4.0.0",
|
|
40
40
|
"nanoid": "^3.1.23",
|
|
41
41
|
"query-string": "^7.0.0",
|
|
42
|
-
"react-is": "^16.13.0"
|
|
42
|
+
"react-is": "^16.13.0",
|
|
43
|
+
"use-latest-callback": "^0.1.5"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"@testing-library/react-native": "^7.2.0",
|
|
46
|
-
"@types/react": "^17.0.
|
|
47
|
+
"@types/react": "^17.0.47",
|
|
47
48
|
"@types/react-is": "^17.0.0",
|
|
48
49
|
"del-cli": "^3.0.1",
|
|
49
50
|
"immer": "^9.0.2",
|
|
50
|
-
"react": "17.0.
|
|
51
|
+
"react": "17.0.2",
|
|
51
52
|
"react-native-builder-bob": "^0.18.1",
|
|
52
|
-
"react-test-renderer": "17.0.
|
|
53
|
-
"typescript": "^4.
|
|
53
|
+
"react-test-renderer": "17.0.2",
|
|
54
|
+
"typescript": "^4.7.4"
|
|
54
55
|
},
|
|
55
56
|
"peerDependencies": {
|
|
56
57
|
"react": "*"
|
|
@@ -69,5 +70,5 @@
|
|
|
69
70
|
]
|
|
70
71
|
]
|
|
71
72
|
},
|
|
72
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "83aa392f1e13796d609c9df0afa44699ce4bacae"
|
|
73
74
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A type of an object that have a route key as an object key
|
|
5
|
+
* and a value whether to prevent that route.
|
|
6
|
+
*/
|
|
7
|
+
export type PreventedRoutes = Record<string, { preventRemove: boolean }>;
|
|
8
|
+
|
|
9
|
+
const PreventRemoveContext = React.createContext<
|
|
10
|
+
| {
|
|
11
|
+
preventedRoutes: PreventedRoutes;
|
|
12
|
+
setPreventRemove: (
|
|
13
|
+
id: string,
|
|
14
|
+
routeKey: string,
|
|
15
|
+
preventRemove: boolean
|
|
16
|
+
) => void;
|
|
17
|
+
}
|
|
18
|
+
| undefined
|
|
19
|
+
>(undefined);
|
|
20
|
+
|
|
21
|
+
export default PreventRemoveContext;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { nanoid } from 'nanoid/non-secure';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import useLatestCallback from 'use-latest-callback';
|
|
4
|
+
|
|
5
|
+
import NavigationHelpersContext from './NavigationHelpersContext';
|
|
6
|
+
import NavigationRouteContext from './NavigationRouteContext';
|
|
7
|
+
import PreventRemoveContext, { PreventedRoutes } from './PreventRemoveContext';
|
|
8
|
+
|
|
9
|
+
type Props = {
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type PreventedRoutesMap = Map<
|
|
14
|
+
string,
|
|
15
|
+
{
|
|
16
|
+
routeKey: string;
|
|
17
|
+
preventRemove: boolean;
|
|
18
|
+
}
|
|
19
|
+
>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Util function to transform map of prevented routes to a simpler object.
|
|
23
|
+
*/
|
|
24
|
+
const transformPreventedRoutes = (
|
|
25
|
+
preventedRoutesMap: PreventedRoutesMap
|
|
26
|
+
): PreventedRoutes => {
|
|
27
|
+
const preventedRoutesToTransform = [...preventedRoutesMap.values()];
|
|
28
|
+
|
|
29
|
+
const preventedRoutes = preventedRoutesToTransform.reduce<PreventedRoutes>(
|
|
30
|
+
(acc, { routeKey, preventRemove }) => {
|
|
31
|
+
acc[routeKey] = {
|
|
32
|
+
preventRemove: acc[routeKey]?.preventRemove || preventRemove,
|
|
33
|
+
};
|
|
34
|
+
return acc;
|
|
35
|
+
},
|
|
36
|
+
{}
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
return preventedRoutes;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Component used for managing which routes have to be prevented from removal in native-stack.
|
|
44
|
+
*/
|
|
45
|
+
export default function PreventRemoveProvider({ children }: Props) {
|
|
46
|
+
const [parentId] = React.useState(() => nanoid());
|
|
47
|
+
const [preventedRoutesMap, setPreventedRoutesMap] =
|
|
48
|
+
React.useState<PreventedRoutesMap>(new Map());
|
|
49
|
+
|
|
50
|
+
const navigation = React.useContext(NavigationHelpersContext);
|
|
51
|
+
const route = React.useContext(NavigationRouteContext);
|
|
52
|
+
|
|
53
|
+
const preventRemoveContextValue = React.useContext(PreventRemoveContext);
|
|
54
|
+
// take `setPreventRemove` from parent context - if exist it means we're in a nested context
|
|
55
|
+
const setParentPrevented = preventRemoveContextValue?.setPreventRemove;
|
|
56
|
+
|
|
57
|
+
const setPreventRemove = useLatestCallback(
|
|
58
|
+
(id: string, routeKey: string, preventRemove: boolean): void => {
|
|
59
|
+
if (
|
|
60
|
+
preventRemove &&
|
|
61
|
+
(navigation == null ||
|
|
62
|
+
navigation
|
|
63
|
+
?.getState()
|
|
64
|
+
.routes.every((route) => route.key !== routeKey))
|
|
65
|
+
) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`Couldn't find a route with the key ${routeKey}. Is your component inside NavigationContent?`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
setPreventedRoutesMap((prevPrevented) => {
|
|
72
|
+
// values haven't changed - do nothing
|
|
73
|
+
if (
|
|
74
|
+
routeKey === prevPrevented.get(id)?.routeKey &&
|
|
75
|
+
preventRemove === prevPrevented.get(id)?.preventRemove
|
|
76
|
+
) {
|
|
77
|
+
return prevPrevented;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const nextPrevented = new Map(prevPrevented);
|
|
81
|
+
|
|
82
|
+
if (preventRemove) {
|
|
83
|
+
nextPrevented.set(id, {
|
|
84
|
+
routeKey,
|
|
85
|
+
preventRemove,
|
|
86
|
+
});
|
|
87
|
+
} else {
|
|
88
|
+
nextPrevented.delete(id);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return nextPrevented;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const isPrevented = [...preventedRoutesMap.values()].some(
|
|
97
|
+
({ preventRemove }) => preventRemove
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
React.useEffect(() => {
|
|
101
|
+
if (route?.key !== undefined && setParentPrevented !== undefined) {
|
|
102
|
+
// when route is defined (and setParentPrevented) it means we're in a nested stack
|
|
103
|
+
// route.key then will be the route key of parent
|
|
104
|
+
setParentPrevented(parentId, route.key, isPrevented);
|
|
105
|
+
return () => {
|
|
106
|
+
setParentPrevented(parentId, route.key, false);
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return;
|
|
111
|
+
}, [parentId, isPrevented, route?.key, setParentPrevented]);
|
|
112
|
+
|
|
113
|
+
const value = React.useMemo(
|
|
114
|
+
() => ({
|
|
115
|
+
setPreventRemove,
|
|
116
|
+
preventedRoutes: transformPreventedRoutes(preventedRoutesMap),
|
|
117
|
+
}),
|
|
118
|
+
[setPreventRemove, preventedRoutesMap]
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<PreventRemoveContext.Provider value={value}>
|
|
123
|
+
{children}
|
|
124
|
+
</PreventRemoveContext.Provider>
|
|
125
|
+
);
|
|
126
|
+
}
|
package/src/getStateFromPath.tsx
CHANGED
|
@@ -565,7 +565,10 @@ const parseQueryParams = (
|
|
|
565
565
|
|
|
566
566
|
if (parseConfig) {
|
|
567
567
|
Object.keys(params).forEach((name) => {
|
|
568
|
-
if (
|
|
568
|
+
if (
|
|
569
|
+
Object.hasOwnProperty.call(parseConfig, name) &&
|
|
570
|
+
typeof params[name] === 'string'
|
|
571
|
+
) {
|
|
569
572
|
params[name] = parseConfig[name](params[name] as string);
|
|
570
573
|
}
|
|
571
574
|
});
|
package/src/index.tsx
CHANGED
|
@@ -11,6 +11,8 @@ export { default as NavigationContainerRefContext } from './NavigationContainerR
|
|
|
11
11
|
export { default as NavigationContext } from './NavigationContext';
|
|
12
12
|
export { default as NavigationHelpersContext } from './NavigationHelpersContext';
|
|
13
13
|
export { default as NavigationRouteContext } from './NavigationRouteContext';
|
|
14
|
+
export { default as PreventRemoveContext } from './PreventRemoveContext';
|
|
15
|
+
export { default as PreventRemoveProvider } from './PreventRemoveProvider';
|
|
14
16
|
export * from './types';
|
|
15
17
|
export { default as useFocusEffect } from './useFocusEffect';
|
|
16
18
|
export { default as useIsFocused } from './useIsFocused';
|
|
@@ -18,6 +20,8 @@ export { default as useNavigation } from './useNavigation';
|
|
|
18
20
|
export { default as useNavigationBuilder } from './useNavigationBuilder';
|
|
19
21
|
export { default as useNavigationContainerRef } from './useNavigationContainerRef';
|
|
20
22
|
export { default as useNavigationState } from './useNavigationState';
|
|
23
|
+
export { default as UNSTABLE_usePreventRemove } from './usePreventRemove';
|
|
24
|
+
export { default as usePreventRemoveContext } from './usePreventRemoveContext';
|
|
21
25
|
export { default as useRoute } from './useRoute';
|
|
22
26
|
export { default as validatePathConfig } from './validatePathConfig';
|
|
23
27
|
export * from '@react-navigation/routers';
|
package/src/types.tsx
CHANGED
|
@@ -192,9 +192,18 @@ type NavigationHelpersCommon<
|
|
|
192
192
|
* @param [params] Params object for the route.
|
|
193
193
|
*/
|
|
194
194
|
navigate<RouteName extends keyof ParamList>(
|
|
195
|
-
...args:
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
...args: // this first condition allows us to iterate over a union type
|
|
196
|
+
// This is to avoid getting a union of all the params from `ParamList[RouteName]`,
|
|
197
|
+
// which will get our types all mixed up if a union RouteName is passed in.
|
|
198
|
+
RouteName extends unknown
|
|
199
|
+
? // This condition checks if the params are optional,
|
|
200
|
+
// which means it's either undefined or a union with undefined
|
|
201
|
+
undefined extends ParamList[RouteName]
|
|
202
|
+
?
|
|
203
|
+
| [screen: RouteName] // if the params are optional, we don't have to provide it
|
|
204
|
+
| [screen: RouteName, params: ParamList[RouteName]]
|
|
205
|
+
: [screen: RouteName, params: ParamList[RouteName]]
|
|
206
|
+
: never
|
|
198
207
|
): void;
|
|
199
208
|
|
|
200
209
|
/**
|
|
@@ -203,14 +212,16 @@ type NavigationHelpersCommon<
|
|
|
203
212
|
* @param route Object with `key` or `name` for the route to navigate to, and a `params` object.
|
|
204
213
|
*/
|
|
205
214
|
navigate<RouteName extends keyof ParamList>(
|
|
206
|
-
options:
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
215
|
+
options: RouteName extends unknown
|
|
216
|
+
?
|
|
217
|
+
| { key: string; params?: ParamList[RouteName]; merge?: boolean }
|
|
218
|
+
| {
|
|
219
|
+
name: RouteName;
|
|
220
|
+
key?: string;
|
|
221
|
+
params: ParamList[RouteName];
|
|
222
|
+
merge?: boolean;
|
|
223
|
+
}
|
|
224
|
+
: never
|
|
214
225
|
): void;
|
|
215
226
|
|
|
216
227
|
/**
|
|
@@ -325,7 +336,11 @@ export type NavigationProp<
|
|
|
325
336
|
*
|
|
326
337
|
* @param params Params object for the current route.
|
|
327
338
|
*/
|
|
328
|
-
setParams(
|
|
339
|
+
setParams(
|
|
340
|
+
params: ParamList[RouteName] extends undefined
|
|
341
|
+
? undefined
|
|
342
|
+
: Partial<ParamList[RouteName]>
|
|
343
|
+
): void;
|
|
329
344
|
|
|
330
345
|
/**
|
|
331
346
|
* Update the options for the route.
|
|
@@ -436,6 +451,16 @@ export type ScreenListeners<
|
|
|
436
451
|
>;
|
|
437
452
|
}>;
|
|
438
453
|
|
|
454
|
+
type ScreenComponentType<
|
|
455
|
+
ParamList extends ParamListBase,
|
|
456
|
+
RouteName extends keyof ParamList
|
|
457
|
+
> =
|
|
458
|
+
| React.ComponentType<{
|
|
459
|
+
route: RouteProp<ParamList, RouteName>;
|
|
460
|
+
navigation: any;
|
|
461
|
+
}>
|
|
462
|
+
| React.ComponentType<{}>;
|
|
463
|
+
|
|
439
464
|
export type RouteConfigComponent<
|
|
440
465
|
ParamList extends ParamListBase,
|
|
441
466
|
RouteName extends keyof ParamList
|
|
@@ -444,7 +469,7 @@ export type RouteConfigComponent<
|
|
|
444
469
|
/**
|
|
445
470
|
* React component to render for this screen.
|
|
446
471
|
*/
|
|
447
|
-
component:
|
|
472
|
+
component: ScreenComponentType<ParamList, RouteName>;
|
|
448
473
|
getComponent?: never;
|
|
449
474
|
children?: never;
|
|
450
475
|
}
|
|
@@ -452,7 +477,7 @@ export type RouteConfigComponent<
|
|
|
452
477
|
/**
|
|
453
478
|
* Lazily get a React component to render for this screen.
|
|
454
479
|
*/
|
|
455
|
-
getComponent: () =>
|
|
480
|
+
getComponent: () => ScreenComponentType<ParamList, RouteName>;
|
|
456
481
|
component?: never;
|
|
457
482
|
children?: never;
|
|
458
483
|
}
|
|
@@ -15,14 +15,16 @@ export default function useChildListeners() {
|
|
|
15
15
|
|
|
16
16
|
const addListener = React.useCallback(
|
|
17
17
|
<T extends keyof ListenerMap>(type: T, listener: ListenerMap[T]) => {
|
|
18
|
-
// @ts-expect-error: listener should be correct type according to `type`
|
|
19
18
|
listeners[type].push(listener);
|
|
20
19
|
|
|
20
|
+
let removed = false;
|
|
21
21
|
return () => {
|
|
22
|
-
// @ts-expect-error: listener should be correct type according to `type`
|
|
23
22
|
const index = listeners[type].indexOf(listener);
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
if (!removed && index > -1) {
|
|
25
|
+
removed = true;
|
|
26
|
+
listeners[type].splice(index, 1);
|
|
27
|
+
}
|
|
26
28
|
};
|
|
27
29
|
},
|
|
28
30
|
[listeners]
|
package/src/useComponent.tsx
CHANGED
|
@@ -1,30 +1,37 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
type Render = (children: React.ReactNode) => JSX.Element;
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
render: Render;
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const NavigationContent = ({ render, children }: Props) => {
|
|
11
|
+
return render(children);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default function useComponent(render: Render) {
|
|
15
|
+
const renderRef = React.useRef<Render | null>(render);
|
|
8
16
|
|
|
9
17
|
// Normally refs shouldn't be mutated in render
|
|
10
18
|
// But we return a component which will be rendered
|
|
11
19
|
// So it's just for immediate consumption
|
|
12
|
-
|
|
20
|
+
renderRef.current = render;
|
|
13
21
|
|
|
14
22
|
React.useEffect(() => {
|
|
15
|
-
|
|
23
|
+
renderRef.current = null;
|
|
16
24
|
});
|
|
17
25
|
|
|
18
|
-
return React.useRef((
|
|
19
|
-
const
|
|
26
|
+
return React.useRef(({ children }: { children: React.ReactNode }) => {
|
|
27
|
+
const render = renderRef.current;
|
|
20
28
|
|
|
21
|
-
if (
|
|
29
|
+
if (render === null) {
|
|
22
30
|
throw new Error(
|
|
23
31
|
'The returned component must be rendered in the same render phase as the hook.'
|
|
24
32
|
);
|
|
25
33
|
}
|
|
26
34
|
|
|
27
|
-
|
|
28
|
-
return <Component {...props} {...rest} />;
|
|
35
|
+
return <NavigationContent render={render}>{children}</NavigationContent>;
|
|
29
36
|
}).current;
|
|
30
37
|
}
|
package/src/useEventEmitter.tsx
CHANGED
|
@@ -21,7 +21,9 @@ export default function useEventEmitter<T extends Record<string, any>>(
|
|
|
21
21
|
listenRef.current = listen;
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
const listeners = React.useRef<Record<string, Record<string, Listeners>>>(
|
|
24
|
+
const listeners = React.useRef<Record<string, Record<string, Listeners>>>(
|
|
25
|
+
Object.create(null)
|
|
26
|
+
);
|
|
25
27
|
|
|
26
28
|
const create = React.useCallback((target: string) => {
|
|
27
29
|
const removeListener = (type: string, callback: (data: any) => void) => {
|
|
@@ -35,7 +37,9 @@ export default function useEventEmitter<T extends Record<string, any>>(
|
|
|
35
37
|
|
|
36
38
|
const index = callbacks.indexOf(callback);
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
if (index > -1) {
|
|
41
|
+
callbacks.splice(index, 1);
|
|
42
|
+
}
|
|
39
43
|
};
|
|
40
44
|
|
|
41
45
|
const addListener = (type: string, callback: (data: any) => void) => {
|
|
@@ -43,7 +47,14 @@ export default function useEventEmitter<T extends Record<string, any>>(
|
|
|
43
47
|
listeners.current[type][target] = listeners.current[type][target] || [];
|
|
44
48
|
listeners.current[type][target].push(callback);
|
|
45
49
|
|
|
46
|
-
|
|
50
|
+
let removed = false;
|
|
51
|
+
return () => {
|
|
52
|
+
// Prevent removing other listeners when unsubscribing same listener multiple times
|
|
53
|
+
if (!removed) {
|
|
54
|
+
removed = true;
|
|
55
|
+
removeListener(type, callback);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
47
58
|
};
|
|
48
59
|
|
|
49
60
|
return {
|
package/src/useFocusEffect.tsx
CHANGED
|
@@ -52,7 +52,7 @@ export default function useFocusEffect(effect: EffectCallback) {
|
|
|
52
52
|
'Instead, write the async function inside your effect ' +
|
|
53
53
|
'and call it immediately:\n\n' +
|
|
54
54
|
'useFocusEffect(\n' +
|
|
55
|
-
' React.useCallback() => {\n' +
|
|
55
|
+
' React.useCallback(() => {\n' +
|
|
56
56
|
' async function fetchData() {\n' +
|
|
57
57
|
' // You can await here\n' +
|
|
58
58
|
' const response = await MyAPI.getData(someId);\n' +
|
|
@@ -11,10 +11,12 @@ export default function useKeyedChildListeners() {
|
|
|
11
11
|
string,
|
|
12
12
|
KeyedListenerMap[K] | undefined
|
|
13
13
|
>;
|
|
14
|
-
}>(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
}>(
|
|
15
|
+
Object.assign(Object.create(null), {
|
|
16
|
+
getState: {},
|
|
17
|
+
beforeRemove: {},
|
|
18
|
+
})
|
|
19
|
+
);
|
|
18
20
|
|
|
19
21
|
const addKeyedListener = React.useCallback(
|
|
20
22
|
<T extends keyof KeyedListenerMap>(
|
|
@@ -22,9 +24,11 @@ export default function useKeyedChildListeners() {
|
|
|
22
24
|
key: string,
|
|
23
25
|
listener: KeyedListenerMap[T]
|
|
24
26
|
) => {
|
|
27
|
+
// @ts-expect-error: according to ref stated above you can use `key` to index type
|
|
25
28
|
keyedListeners[type][key] = listener;
|
|
26
29
|
|
|
27
30
|
return () => {
|
|
31
|
+
// @ts-expect-error: according to ref stated above you can use `key` to index type
|
|
28
32
|
keyedListeners[type][key] = undefined;
|
|
29
33
|
};
|
|
30
34
|
},
|
|
@@ -19,6 +19,7 @@ import isRecordEqual from './isRecordEqual';
|
|
|
19
19
|
import NavigationHelpersContext from './NavigationHelpersContext';
|
|
20
20
|
import NavigationRouteContext from './NavigationRouteContext';
|
|
21
21
|
import NavigationStateContext from './NavigationStateContext';
|
|
22
|
+
import PreventRemoveProvider from './PreventRemoveProvider';
|
|
22
23
|
import Screen from './Screen';
|
|
23
24
|
import {
|
|
24
25
|
DefaultNavigatorOptions,
|
|
@@ -690,9 +691,11 @@ export default function useNavigationBuilder<
|
|
|
690
691
|
descriptors,
|
|
691
692
|
});
|
|
692
693
|
|
|
693
|
-
const NavigationContent = useComponent(
|
|
694
|
-
value
|
|
695
|
-
|
|
694
|
+
const NavigationContent = useComponent((children: React.ReactNode) => (
|
|
695
|
+
<NavigationHelpersContext.Provider value={navigation}>
|
|
696
|
+
<PreventRemoveProvider>{children}</PreventRemoveProvider>
|
|
697
|
+
</NavigationHelpersContext.Provider>
|
|
698
|
+
));
|
|
696
699
|
|
|
697
700
|
return {
|
|
698
701
|
state,
|
|
@@ -95,12 +95,6 @@ export default function useNavigationHelpers<
|
|
|
95
95
|
current = current.getParent();
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
if (current == null) {
|
|
99
|
-
throw new Error(
|
|
100
|
-
`Couldn't find a parent navigator with the ID "${id}". Is this navigator nested under another navigator with this ID?`
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
98
|
return current;
|
|
105
99
|
}
|
|
106
100
|
|