@react-navigation/native-stack 6.7.0 → 6.8.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/lib/commonjs/utils/useDismissedRouteError.js +29 -0
- package/lib/commonjs/utils/useDismissedRouteError.js.map +1 -0
- package/lib/commonjs/utils/useInvalidPreventRemoveError.js +33 -0
- package/lib/commonjs/utils/useInvalidPreventRemoveError.js.map +1 -0
- package/lib/commonjs/views/HeaderConfig.js +1 -1
- package/lib/commonjs/views/HeaderConfig.js.map +1 -1
- package/lib/commonjs/views/NativeStackView.js +9 -5
- package/lib/commonjs/views/NativeStackView.js.map +1 -1
- package/lib/commonjs/views/NativeStackView.native.js +50 -17
- package/lib/commonjs/views/NativeStackView.native.js.map +1 -1
- package/lib/module/utils/useDismissedRouteError.js +17 -0
- package/lib/module/utils/useDismissedRouteError.js.map +1 -0
- package/lib/module/utils/useInvalidPreventRemoveError.js +20 -0
- package/lib/module/utils/useInvalidPreventRemoveError.js.map +1 -0
- package/lib/module/views/HeaderConfig.js +1 -1
- package/lib/module/views/HeaderConfig.js.map +1 -1
- package/lib/module/views/NativeStackView.js +10 -6
- package/lib/module/views/NativeStackView.js.map +1 -1
- package/lib/module/views/NativeStackView.native.js +50 -19
- package/lib/module/views/NativeStackView.native.js.map +1 -1
- package/lib/typescript/src/utils/useDismissedRouteError.d.ts +5 -0
- package/lib/typescript/src/utils/useInvalidPreventRemoveError.d.ts +2 -0
- package/package.json +7 -5
- package/src/utils/useDismissedRouteError.tsx +30 -0
- package/src/utils/useInvalidPreventRemoveError.tsx +31 -0
- package/src/views/HeaderConfig.tsx +1 -1
- package/src/views/NativeStackView.native.tsx +59 -27
- package/src/views/NativeStackView.tsx +20 -12
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getDefaultHeaderHeight,
|
|
3
3
|
getHeaderTitle,
|
|
4
|
+
HeaderBackContext,
|
|
4
5
|
HeaderHeightContext,
|
|
5
6
|
HeaderShownContext,
|
|
6
7
|
SafeAreaProviderCompat,
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
Route,
|
|
13
14
|
StackActions,
|
|
14
15
|
StackNavigationState,
|
|
16
|
+
usePreventRemoveContext,
|
|
15
17
|
useTheme,
|
|
16
18
|
} from '@react-navigation/native';
|
|
17
19
|
import * as React from 'react';
|
|
@@ -34,6 +36,8 @@ import type {
|
|
|
34
36
|
NativeStackNavigationHelpers,
|
|
35
37
|
NativeStackNavigationOptions,
|
|
36
38
|
} from '../types';
|
|
39
|
+
import useDismissedRouteError from '../utils/useDismissedRouteError';
|
|
40
|
+
import useInvalidPreventRemoveError from '../utils/useInvalidPreventRemoveError';
|
|
37
41
|
import DebugContainer from './DebugContainer';
|
|
38
42
|
import HeaderConfig from './HeaderConfig';
|
|
39
43
|
|
|
@@ -116,6 +120,8 @@ type SceneViewProps = {
|
|
|
116
120
|
onAppear: () => void;
|
|
117
121
|
onDisappear: () => void;
|
|
118
122
|
onDismissed: ScreenProps['onDismissed'];
|
|
123
|
+
onHeaderBackButtonClicked: () => void;
|
|
124
|
+
onNativeDismissCancelled: ScreenProps['onDismissed'];
|
|
119
125
|
};
|
|
120
126
|
|
|
121
127
|
const SceneView = ({
|
|
@@ -127,6 +133,8 @@ const SceneView = ({
|
|
|
127
133
|
onAppear,
|
|
128
134
|
onDisappear,
|
|
129
135
|
onDismissed,
|
|
136
|
+
onHeaderBackButtonClicked,
|
|
137
|
+
onNativeDismissCancelled,
|
|
130
138
|
}: SceneViewProps) => {
|
|
131
139
|
const { route, navigation, options, render } = descriptor;
|
|
132
140
|
const {
|
|
@@ -134,6 +142,7 @@ const SceneView = ({
|
|
|
134
142
|
animationTypeForReplace = 'push',
|
|
135
143
|
gestureEnabled,
|
|
136
144
|
header,
|
|
145
|
+
headerBackButtonMenuEnabled,
|
|
137
146
|
headerShown,
|
|
138
147
|
autoHideHomeIndicator,
|
|
139
148
|
navigationBarColor,
|
|
@@ -202,6 +211,9 @@ const SceneView = ({
|
|
|
202
211
|
|
|
203
212
|
const isParentHeaderShown = React.useContext(HeaderShownContext);
|
|
204
213
|
const parentHeaderHeight = React.useContext(HeaderHeightContext);
|
|
214
|
+
const parentHeaderBack = React.useContext(HeaderBackContext);
|
|
215
|
+
|
|
216
|
+
const { preventedRoutes } = usePreventRemoveContext();
|
|
205
217
|
|
|
206
218
|
const defaultHeaderHeight = getDefaultHeaderHeight(frame, isModal, topInset);
|
|
207
219
|
|
|
@@ -209,6 +221,16 @@ const SceneView = ({
|
|
|
209
221
|
React.useState(defaultHeaderHeight);
|
|
210
222
|
|
|
211
223
|
const headerHeight = header ? customHeaderHeight : defaultHeaderHeight;
|
|
224
|
+
const headerBack = previousDescriptor
|
|
225
|
+
? {
|
|
226
|
+
title: getHeaderTitle(
|
|
227
|
+
previousDescriptor.options,
|
|
228
|
+
previousDescriptor.route.name
|
|
229
|
+
),
|
|
230
|
+
}
|
|
231
|
+
: parentHeaderBack;
|
|
232
|
+
|
|
233
|
+
const isRemovePrevented = preventedRoutes[route.key]?.preventRemove;
|
|
212
234
|
|
|
213
235
|
return (
|
|
214
236
|
<Screen
|
|
@@ -243,6 +265,12 @@ const SceneView = ({
|
|
|
243
265
|
onDisappear={onDisappear}
|
|
244
266
|
onDismissed={onDismissed}
|
|
245
267
|
isNativeStack
|
|
268
|
+
// Props for enabling preventing removal in native-stack
|
|
269
|
+
nativeBackButtonDismissalEnabled={false} // on Android
|
|
270
|
+
// @ts-expect-error prop not publicly exported from rn-screens
|
|
271
|
+
preventNativeDismiss={isRemovePrevented} // on iOS
|
|
272
|
+
onHeaderBackButtonClicked={onHeaderBackButtonClicked}
|
|
273
|
+
onNativeDismissCancelled={onNativeDismissCancelled}
|
|
246
274
|
>
|
|
247
275
|
<NavigationContext.Provider value={navigation}>
|
|
248
276
|
<NavigationRouteContext.Provider value={route}>
|
|
@@ -263,14 +291,7 @@ const SceneView = ({
|
|
|
263
291
|
}}
|
|
264
292
|
>
|
|
265
293
|
{header({
|
|
266
|
-
back:
|
|
267
|
-
? {
|
|
268
|
-
title: getHeaderTitle(
|
|
269
|
-
previousDescriptor.options,
|
|
270
|
-
previousDescriptor.route.name
|
|
271
|
-
),
|
|
272
|
-
}
|
|
273
|
-
: undefined,
|
|
294
|
+
back: headerBack,
|
|
274
295
|
options,
|
|
275
296
|
route,
|
|
276
297
|
navigation,
|
|
@@ -280,9 +301,19 @@ const SceneView = ({
|
|
|
280
301
|
<HeaderConfig
|
|
281
302
|
{...options}
|
|
282
303
|
route={route}
|
|
304
|
+
headerBackButtonMenuEnabled={
|
|
305
|
+
isRemovePrevented !== undefined
|
|
306
|
+
? !isRemovePrevented
|
|
307
|
+
: headerBackButtonMenuEnabled
|
|
308
|
+
}
|
|
283
309
|
headerShown={isHeaderInPush}
|
|
284
310
|
headerHeight={headerHeight}
|
|
285
|
-
|
|
311
|
+
headerBackTitle={
|
|
312
|
+
options.headerBackTitle !== undefined
|
|
313
|
+
? options.headerBackTitle
|
|
314
|
+
: headerBack?.title
|
|
315
|
+
}
|
|
316
|
+
canGoBack={headerBack !== undefined}
|
|
286
317
|
/>
|
|
287
318
|
)}
|
|
288
319
|
<MaybeNestedStack
|
|
@@ -291,7 +322,9 @@ const SceneView = ({
|
|
|
291
322
|
presentation={presentation}
|
|
292
323
|
headerHeight={headerHeight}
|
|
293
324
|
>
|
|
294
|
-
{
|
|
325
|
+
<HeaderBackContext.Provider value={headerBack}>
|
|
326
|
+
{render()}
|
|
327
|
+
</HeaderBackContext.Provider>
|
|
295
328
|
</MaybeNestedStack>
|
|
296
329
|
</HeaderHeightContext.Provider>
|
|
297
330
|
</HeaderShownContext.Provider>
|
|
@@ -308,24 +341,9 @@ type Props = {
|
|
|
308
341
|
};
|
|
309
342
|
|
|
310
343
|
function NativeStackViewInner({ state, navigation, descriptors }: Props) {
|
|
311
|
-
const
|
|
312
|
-
null
|
|
313
|
-
);
|
|
314
|
-
|
|
315
|
-
const dismissedRouteName = nextDismissedKey
|
|
316
|
-
? state.routes.find((route) => route.key === nextDismissedKey)?.name
|
|
317
|
-
: null;
|
|
344
|
+
const { setNextDismissedKey } = useDismissedRouteError(state);
|
|
318
345
|
|
|
319
|
-
|
|
320
|
-
if (dismissedRouteName) {
|
|
321
|
-
const message =
|
|
322
|
-
`The screen '${dismissedRouteName}' was removed natively but didn't get removed from JS state. ` +
|
|
323
|
-
`This can happen if the action was prevented in a 'beforeRemove' listener, which is not fully supported in native-stack.\n\n` +
|
|
324
|
-
`Consider using 'gestureEnabled: false' to prevent back gesture and use a custom back button with 'headerLeft' option to override the native behavior.`;
|
|
325
|
-
|
|
326
|
-
console.error(message);
|
|
327
|
-
}
|
|
328
|
-
}, [dismissedRouteName]);
|
|
346
|
+
useInvalidPreventRemoveError(descriptors);
|
|
329
347
|
|
|
330
348
|
return (
|
|
331
349
|
<ScreenStack style={styles.container}>
|
|
@@ -375,6 +393,20 @@ function NativeStackViewInner({ state, navigation, descriptors }: Props) {
|
|
|
375
393
|
|
|
376
394
|
setNextDismissedKey(route.key);
|
|
377
395
|
}}
|
|
396
|
+
onHeaderBackButtonClicked={() => {
|
|
397
|
+
navigation.dispatch({
|
|
398
|
+
...StackActions.pop(),
|
|
399
|
+
source: route.key,
|
|
400
|
+
target: state.key,
|
|
401
|
+
});
|
|
402
|
+
}}
|
|
403
|
+
onNativeDismissCancelled={(event) => {
|
|
404
|
+
navigation.dispatch({
|
|
405
|
+
...StackActions.pop(event.nativeEvent.dismissCount),
|
|
406
|
+
source: route.key,
|
|
407
|
+
target: state.key,
|
|
408
|
+
});
|
|
409
|
+
}}
|
|
378
410
|
/>
|
|
379
411
|
);
|
|
380
412
|
})}
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
getHeaderTitle,
|
|
3
3
|
Header,
|
|
4
4
|
HeaderBackButton,
|
|
5
|
+
HeaderBackContext,
|
|
5
6
|
SafeAreaProviderCompat,
|
|
6
7
|
Screen,
|
|
7
8
|
} from '@react-navigation/elements';
|
|
@@ -31,12 +32,13 @@ const TRANSPARENT_PRESENTATIONS = [
|
|
|
31
32
|
];
|
|
32
33
|
|
|
33
34
|
export default function NativeStackView({ state, descriptors }: Props) {
|
|
35
|
+
const parentHeaderBack = React.useContext(HeaderBackContext);
|
|
36
|
+
|
|
34
37
|
return (
|
|
35
38
|
<SafeAreaProviderCompat>
|
|
36
39
|
<View style={styles.container}>
|
|
37
40
|
{state.routes.map((route, i) => {
|
|
38
41
|
const isFocused = state.index === i;
|
|
39
|
-
const canGoBack = i !== 0;
|
|
40
42
|
const previousKey = state.routes[i - 1]?.key;
|
|
41
43
|
const nextKey = state.routes[i + 1]?.key;
|
|
42
44
|
const previousDescriptor = previousKey
|
|
@@ -45,6 +47,17 @@ export default function NativeStackView({ state, descriptors }: Props) {
|
|
|
45
47
|
const nextDescriptor = nextKey ? descriptors[nextKey] : undefined;
|
|
46
48
|
const { options, navigation, render } = descriptors[route.key];
|
|
47
49
|
|
|
50
|
+
const headerBack = previousDescriptor
|
|
51
|
+
? {
|
|
52
|
+
title: getHeaderTitle(
|
|
53
|
+
previousDescriptor.options,
|
|
54
|
+
previousDescriptor.route.name
|
|
55
|
+
),
|
|
56
|
+
}
|
|
57
|
+
: parentHeaderBack;
|
|
58
|
+
|
|
59
|
+
const canGoBack = headerBack !== undefined;
|
|
60
|
+
|
|
48
61
|
const {
|
|
49
62
|
header,
|
|
50
63
|
headerShown,
|
|
@@ -77,14 +90,7 @@ export default function NativeStackView({ state, descriptors }: Props) {
|
|
|
77
90
|
header={
|
|
78
91
|
header !== undefined ? (
|
|
79
92
|
header({
|
|
80
|
-
back:
|
|
81
|
-
? {
|
|
82
|
-
title: getHeaderTitle(
|
|
83
|
-
previousDescriptor.options,
|
|
84
|
-
previousDescriptor.route.name
|
|
85
|
-
),
|
|
86
|
-
}
|
|
87
|
-
: undefined,
|
|
93
|
+
back: headerBack,
|
|
88
94
|
options,
|
|
89
95
|
route,
|
|
90
96
|
navigation,
|
|
@@ -161,9 +167,11 @@ export default function NativeStackView({ state, descriptors }: Props) {
|
|
|
161
167
|
: null,
|
|
162
168
|
]}
|
|
163
169
|
>
|
|
164
|
-
<
|
|
165
|
-
{
|
|
166
|
-
|
|
170
|
+
<HeaderBackContext.Provider value={headerBack}>
|
|
171
|
+
<View style={[styles.contentContainer, contentStyle]}>
|
|
172
|
+
{render()}
|
|
173
|
+
</View>
|
|
174
|
+
</HeaderBackContext.Provider>
|
|
167
175
|
</Screen>
|
|
168
176
|
);
|
|
169
177
|
})}
|