mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-13 22:42:25 +08:00
Compare commits
19 Commits
@react-nav
...
@react-nav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50a161dc3d | ||
|
|
360b0e9958 | ||
|
|
e50c8aa942 | ||
|
|
8f0efc8db5 | ||
|
|
7de6677e72 | ||
|
|
1dad338b7a | ||
|
|
ce7d20e336 | ||
|
|
e3e58c2d89 | ||
|
|
cb2e744dce | ||
|
|
9beca3a802 | ||
|
|
ec7b02af2c | ||
|
|
4c2379cec1 | ||
|
|
1169ed0946 | ||
|
|
bf464a8378 | ||
|
|
a495506e20 | ||
|
|
b20f2d1f7c | ||
|
|
66f3a4a0bb | ||
|
|
84cc0d758a | ||
|
|
ebc7f9ea75 |
10
.github/workflows/triage.yml
vendored
10
.github/workflows/triage.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "Hey! Thanks for opening the issue. Can you provide more information about the issue? Please fill the issue template when opening the issue without deleting any section. We need all the information we can to be able to help.\n\nMake sure to at least provide - Current behaviour, Expected behaviour, A way to [reproduce the issue with minimal code](https://stackoverflow.com/help/minimal-reproducible-example) (link to [snack.expo.io](https://snack.expo.io)) or a repo on GitHub, and the information about your environment (such as the platform of the device, exact versions of all the packages mentioned in the template etc.)."
|
||||
body: "Hey! Thanks for opening the issue. Can you provide more information about the issue? Please fill the issue template when opening the issue without deleting any section. We need all the information we can to be able to help.\n\nMake sure to at least provide - Current behaviour, Expected behaviour, A way to [reproduce the issue with minimal code](https://stackoverflow.com/help/minimal-reproducible-example) (link to [snack.expo.io](https://snack.expo.io)) or a repo on GitHub, and the information about your environment (such as the platform of the device, exact versions of all the packages mentioned in the template etc.). In addition, if you can provide a video or GIF demonstrating the issue, it will also be very helpful."
|
||||
})
|
||||
|
||||
needs-repro:
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "Hey! Thanks for opening the issue. The issue tracker is intended for only tracking bug reports in React Navigation. Seems you have an issue related to `native-stack` navigator or `react-native-screens` library. Please post your issue in [this repo](https://github.com/software-mansion/react-native-screens) so that it's notified to the maintainers of that library."
|
||||
body: "Hey! Thanks for opening the issue. Seems that this issue issue related to `react-native-screens` library which is a dependency of React Navigation. Can you also post your issue in [this repo](https://github.com/software-mansion/react-native-screens) so that it's notified to the maintainers of that library? This will help us fix the issue faster since it's upto the maintainers of that library to investigate it."
|
||||
})
|
||||
|
||||
react-native-reanimated:
|
||||
@@ -91,7 +91,7 @@ jobs:
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "Hey! Thanks for opening the issue. The issue tracker is intended for only tracking bug reports in React Navigation. Seems you have an issue related to `react-native-reanimated` library. Please post your issue in [this repo](https://github.com/software-mansion/react-native-reanimated) so that it's notified to the maintainers of that library."
|
||||
body: "Hey! Thanks for opening the issue. Seems that this issue issue related to `react-native-reanimated` library which is a dependency of React Navigation. Can you also post your issue in [this repo](https://github.com/software-mansion/react-native-reanimated) so that it's notified to the maintainers of that library? This will help us fix the issue faster since it's upto the maintainers of that library to investigate it."
|
||||
})
|
||||
|
||||
react-native-gesture-handler:
|
||||
@@ -106,7 +106,7 @@ jobs:
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "Hey! Thanks for opening the issue. The issue tracker is intended for only tracking bug reports in React Navigation. Seems you have an issue related to `react-native-gesture-handler` library. Please post your issue in [this repo](https://github.com/software-mansion/react-native-gesture-handler) so that it's notified to the maintainers of that library."
|
||||
body: "Hey! Thanks for opening the issue. Seems that this issue issue related to `react-native-gesture-handler` library which is a dependency of React Navigation. Can you also post your issue in [this repo](https://github.com/software-mansion/react-native-gesture-handler) so that it's notified to the maintainers of that library? This will help us fix the issue faster since it's upto the maintainers of that library to investigate it."
|
||||
})
|
||||
|
||||
react-native-safe-area-context:
|
||||
@@ -121,5 +121,5 @@ jobs:
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "Hey! Thanks for opening the issue. The issue tracker is intended for only tracking bug reports in React Navigation. Seems you have an issue related to `react-native-safe-area-context` library. Please post your issue in [this repo](https://github.com/th3rdwave/react-native-safe-area-context) so that it's notified to the maintainers of that library."
|
||||
body: "Hey! Thanks for opening the issue. Seems that this issue issue related to `react-native-safe-area-context` library which is a dependency of React Navigation. Can you also post your issue in [this repo](https://github.com/th3rdwave/react-native-safe-area-context) so that it's notified to the maintainers of that library? This will help us fix the issue faster since it's upto the maintainers of that library to investigate it."
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@ import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityI
|
||||
import {
|
||||
getFocusedRouteNameFromRoute,
|
||||
ParamListBase,
|
||||
NavigatorScreenParams,
|
||||
} from '@react-navigation/native';
|
||||
import type { StackScreenProps } from '@react-navigation/stack';
|
||||
import {
|
||||
@@ -15,7 +16,7 @@ import TouchableBounce from '../Shared/TouchableBounce';
|
||||
import Albums from '../Shared/Albums';
|
||||
import Contacts from '../Shared/Contacts';
|
||||
import Chat from '../Shared/Chat';
|
||||
import SimpleStackScreen from './SimpleStack';
|
||||
import SimpleStackScreen, { SimpleStackParams } from './SimpleStack';
|
||||
|
||||
const getTabBarIcon = (name: string) => ({
|
||||
color,
|
||||
@@ -26,7 +27,7 @@ const getTabBarIcon = (name: string) => ({
|
||||
}) => <MaterialCommunityIcons name={name} color={color} size={size} />;
|
||||
|
||||
type BottomTabParams = {
|
||||
Article: undefined;
|
||||
Article: NavigatorScreenParams<SimpleStackParams>;
|
||||
Albums: undefined;
|
||||
Contacts: undefined;
|
||||
Chat: undefined;
|
||||
@@ -85,12 +86,18 @@ export default function BottomTabsScreen({
|
||||
>
|
||||
<BottomTabs.Screen
|
||||
name="Article"
|
||||
component={SimpleStackScreen}
|
||||
options={{
|
||||
title: 'Article',
|
||||
tabBarIcon: getTabBarIcon('file-document-box'),
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{(props) => (
|
||||
<SimpleStackScreen
|
||||
{...props}
|
||||
screenOptions={{ headerShown: false }}
|
||||
/>
|
||||
)}
|
||||
</BottomTabs.Screen>
|
||||
<BottomTabs.Screen
|
||||
name="Chat"
|
||||
component={Chat}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import type { NavigatorScreenParams } from '@react-navigation/native';
|
||||
import { createMaterialBottomTabNavigator } from '@react-navigation/material-bottom-tabs';
|
||||
import Albums from '../Shared/Albums';
|
||||
import Contacts from '../Shared/Contacts';
|
||||
import Chat from '../Shared/Chat';
|
||||
import SimpleStackScreen from './SimpleStack';
|
||||
import SimpleStackScreen, { SimpleStackParams } from './SimpleStack';
|
||||
|
||||
type MaterialBottomTabParams = {
|
||||
Article: undefined;
|
||||
Article: NavigatorScreenParams<SimpleStackParams>;
|
||||
Albums: undefined;
|
||||
Contacts: undefined;
|
||||
Chat: undefined;
|
||||
@@ -22,13 +23,19 @@ export default function MaterialBottomTabsScreen() {
|
||||
<MaterialBottomTabs.Navigator barStyle={styles.tabBar}>
|
||||
<MaterialBottomTabs.Screen
|
||||
name="Article"
|
||||
component={SimpleStackScreen}
|
||||
options={{
|
||||
tabBarLabel: 'Article',
|
||||
tabBarIcon: 'file-document-box',
|
||||
tabBarColor: '#C9E7F8',
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{(props) => (
|
||||
<SimpleStackScreen
|
||||
{...props}
|
||||
screenOptions={{ headerShown: false }}
|
||||
/>
|
||||
)}
|
||||
</MaterialBottomTabs.Screen>
|
||||
<MaterialBottomTabs.Screen
|
||||
name="Chat"
|
||||
component={Chat}
|
||||
|
||||
@@ -93,7 +93,9 @@ export default function SimpleStackScreen({ navigation, options }: Props) {
|
||||
cardOverlayEnabled: true,
|
||||
gestureEnabled: true,
|
||||
headerStatusBarHeight:
|
||||
navigation.dangerouslyGetState().routes.indexOf(route) > 0
|
||||
navigation
|
||||
.dangerouslyGetState()
|
||||
.routes.findIndex((r: any) => r.key === route.key) > 0
|
||||
? 0
|
||||
: undefined,
|
||||
})}
|
||||
|
||||
@@ -77,18 +77,28 @@ const InputScreen = ({
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
Alert.alert(
|
||||
'Discard changes?',
|
||||
'You have unsaved changes. Are you sure to discard them and leave the screen?',
|
||||
[
|
||||
{ text: "Don't leave", style: 'cancel', onPress: () => {} },
|
||||
{
|
||||
text: 'Discard',
|
||||
style: 'destructive',
|
||||
onPress: () => navigation.dispatch(action),
|
||||
},
|
||||
]
|
||||
);
|
||||
if (Platform.OS === 'web') {
|
||||
const discard = confirm(
|
||||
'You have unsaved changes. Discard them and leave the screen?'
|
||||
);
|
||||
|
||||
if (discard) {
|
||||
navigation.dispatch(action);
|
||||
}
|
||||
} else {
|
||||
Alert.alert(
|
||||
'Discard changes?',
|
||||
'You have unsaved changes. Discard them and leave the screen?',
|
||||
[
|
||||
{ text: "Don't leave", style: 'cancel', onPress: () => {} },
|
||||
{
|
||||
text: 'Discard',
|
||||
style: 'destructive',
|
||||
onPress: () => navigation.dispatch(action),
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}),
|
||||
[hasUnsavedChanges, navigation]
|
||||
);
|
||||
|
||||
@@ -4,13 +4,14 @@ import { Button } from 'react-native-paper';
|
||||
import type { ParamListBase } from '@react-navigation/native';
|
||||
import {
|
||||
createStackNavigator,
|
||||
StackNavigationOptions,
|
||||
StackScreenProps,
|
||||
} from '@react-navigation/stack';
|
||||
import Article from '../Shared/Article';
|
||||
import Albums from '../Shared/Albums';
|
||||
import NewsFeed from '../Shared/NewsFeed';
|
||||
|
||||
type SimpleStackParams = {
|
||||
export type SimpleStackParams = {
|
||||
Article: { author: string } | undefined;
|
||||
NewsFeed: { date: number };
|
||||
Albums: undefined;
|
||||
@@ -105,7 +106,10 @@ const SimpleStack = createStackNavigator<SimpleStackParams>();
|
||||
|
||||
export default function SimpleStackScreen({
|
||||
navigation,
|
||||
}: StackScreenProps<ParamListBase>) {
|
||||
screenOptions,
|
||||
}: StackScreenProps<ParamListBase> & {
|
||||
screenOptions?: StackNavigationOptions;
|
||||
}) {
|
||||
React.useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerShown: false,
|
||||
@@ -113,7 +117,7 @@ export default function SimpleStackScreen({
|
||||
}, [navigation]);
|
||||
|
||||
return (
|
||||
<SimpleStack.Navigator>
|
||||
<SimpleStack.Navigator screenOptions={screenOptions}>
|
||||
<SimpleStack.Screen
|
||||
name="Article"
|
||||
component={ArticleScreen}
|
||||
|
||||
@@ -3,6 +3,41 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.10.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.10.6...@react-navigation/bottom-tabs@5.10.7) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.10.5...@react-navigation/bottom-tabs@5.10.6) (2020-11-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* disable react-native-screens on iOS for older versions ([ce7d20e](https://github.com/react-navigation/react-navigation/commit/ce7d20e3366415b07a537e01ee0b17ce7e72cad6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.10.4...@react-navigation/bottom-tabs@5.10.5) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.10.3...@react-navigation/bottom-tabs@5.10.4) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/bottom-tabs@5.10.2...@react-navigation/bottom-tabs@5.10.3) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/bottom-tabs
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/bottom-tabs",
|
||||
"description": "Bottom tab navigator following iOS design guidelines",
|
||||
"version": "5.10.3",
|
||||
"version": "5.10.7",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -41,7 +41,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"@react-navigation/native": "^5.8.3",
|
||||
"@react-navigation/native": "^5.8.7",
|
||||
"@testing-library/react-native": "^7.1.0",
|
||||
"@types/color": "^3.0.1",
|
||||
"@types/react": "^16.9.53",
|
||||
|
||||
@@ -174,7 +174,7 @@ export type BottomTabNavigationConfig<T = BottomTabBarOptions> = {
|
||||
/**
|
||||
* Whether inactive screens should be detached from the view hierarchy to save memory.
|
||||
* Make sure to call `enableScreens` from `react-native-screens` to make it work.
|
||||
* Defaults to `true`.
|
||||
* Defaults to `true` on Android.
|
||||
*/
|
||||
detachInactiveScreens?: boolean;
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.3.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.6...@react-navigation/compat@5.3.7) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.5...@react-navigation/compat@5.3.6) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.4...@react-navigation/compat@5.3.5) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.3...@react-navigation/compat@5.3.4) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/compat@5.3.2...@react-navigation/compat@5.3.3) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/compat
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/compat",
|
||||
"description": "Compatibility layer to write navigator definitions in static configuration format",
|
||||
"version": "5.3.3",
|
||||
"version": "5.3.7",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -32,7 +32,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"@react-navigation/native": "^5.8.3",
|
||||
"@react-navigation/native": "^5.8.7",
|
||||
"@types/react": "^16.9.53",
|
||||
"react": "~16.13.1",
|
||||
"typescript": "^4.0.3"
|
||||
|
||||
@@ -147,7 +147,6 @@ export default function createCompatNavigationProp<
|
||||
}
|
||||
},
|
||||
state: {
|
||||
// @ts-expect-error: these properties may actually exist
|
||||
key: state.key,
|
||||
// @ts-expect-error
|
||||
routeName: state.name,
|
||||
@@ -202,7 +201,6 @@ export default function createCompatNavigationProp<
|
||||
|
||||
const { routes } = navigation.dangerouslyGetState();
|
||||
|
||||
// @ts-expect-error
|
||||
return routes[0].key === state.key;
|
||||
},
|
||||
dangerouslyGetParent() {
|
||||
|
||||
@@ -3,6 +3,57 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.14.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.14.0...@react-navigation/core@5.14.1) (2020-11-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* tweak error message when navigator has non-screen children ([360b0e9](https://github.com/react-navigation/react-navigation/commit/360b0e995835990c55b75898757ebdd120d52446))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.14.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.13.5...@react-navigation/core@5.14.0) (2020-11-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* always respect key in the route object when generating action ([cb2e744](https://github.com/react-navigation/react-navigation/commit/cb2e744dcebf7f71ddaa5462d393a6dbfd971fcd))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add a NavigatorScreenParams type. closes [#6931](https://github.com/react-navigation/react-navigation/issues/6931) ([e3e58c2](https://github.com/react-navigation/react-navigation/commit/e3e58c2d890e7fab75d78371e349aea55a402fcd))
|
||||
* add warning on accessing the state object on route prop ([ec7b02a](https://github.com/react-navigation/react-navigation/commit/ec7b02af2ca835122b9000799e2366d7009da6e3))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.13.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.13.4...@react-navigation/core@5.13.5) (2020-11-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* don't use use-subscription to avoid peer dep related errors ([66f3a4a](https://github.com/react-navigation/react-navigation/commit/66f3a4a0bb39475434668bc94fb1750dbe618ee0)), closes [/github.com/react-navigation/react-navigation/issues/9021#issuecomment-721679760](https://github.com//github.com/react-navigation/react-navigation/issues/9021/issues/issuecomment-721679760)
|
||||
* use useDebugValue in more places ([b20f2d1](https://github.com/react-navigation/react-navigation/commit/b20f2d1f7ccb82db70df9cddf5746557912daa99))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.13.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.13.3...@react-navigation/core@5.13.4) (2020-11-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix nested navigation not working the first time ([ebc7f9e](https://github.com/react-navigation/react-navigation/commit/ebc7f9ea75bbf6e3b6303027cfa023d7c97342ff))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.13.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/core@5.13.2...@react-navigation/core@5.13.3) (2020-11-03)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/core",
|
||||
"description": "Core utilities for building navigators",
|
||||
"version": "5.13.3",
|
||||
"version": "5.14.1",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-native",
|
||||
@@ -35,19 +35,17 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/routers": "^5.5.1",
|
||||
"@react-navigation/routers": "^5.6.1",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"nanoid": "^3.1.15",
|
||||
"query-string": "^6.13.6",
|
||||
"react-is": "^16.13.0",
|
||||
"use-subscription": "^1.5.0"
|
||||
"react-is": "^16.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"@testing-library/react-native": "^7.1.0",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-is": "^16.7.1",
|
||||
"@types/use-subscription": "^1.0.0",
|
||||
"del-cli": "^3.0.1",
|
||||
"react": "~16.13.1",
|
||||
"react-test-renderer": "~16.13.1",
|
||||
|
||||
@@ -160,9 +160,20 @@ const BaseNavigationContainer = React.forwardRef(
|
||||
|
||||
const resetRoot = React.useCallback(
|
||||
(state?: PartialState<NavigationState> | NavigationState) => {
|
||||
setState(state);
|
||||
const target = state?.key ?? keyedListeners.getState.root?.().key;
|
||||
|
||||
if (target == null) {
|
||||
throw new Error(NOT_INITIALIZED_ERROR);
|
||||
}
|
||||
|
||||
listeners.focus[0]((navigation) =>
|
||||
navigation.dispatch({
|
||||
...CommonActions.reset(state),
|
||||
target,
|
||||
})
|
||||
);
|
||||
},
|
||||
[setState]
|
||||
[keyedListeners.getState, listeners.focus]
|
||||
);
|
||||
|
||||
const getRootState = React.useCallback(() => {
|
||||
|
||||
@@ -18,9 +18,8 @@ type Props<
|
||||
> = {
|
||||
screen: RouteConfig<ParamListBase, string, State, ScreenOptions, EventMap>;
|
||||
navigation: NavigationProp<ParamListBase, string, State, ScreenOptions>;
|
||||
route: Route<string> & {
|
||||
state?: NavigationState | PartialState<NavigationState>;
|
||||
};
|
||||
route: Route<string>;
|
||||
routeState: NavigationState | PartialState<NavigationState> | undefined;
|
||||
getState: () => State;
|
||||
setState: (state: State) => void;
|
||||
options: object;
|
||||
@@ -38,6 +37,7 @@ export default function SceneView<
|
||||
screen,
|
||||
route,
|
||||
navigation,
|
||||
routeState,
|
||||
getState,
|
||||
setState,
|
||||
options,
|
||||
@@ -86,7 +86,7 @@ export default function SceneView<
|
||||
|
||||
const context = React.useMemo(
|
||||
() => ({
|
||||
state: route.state,
|
||||
state: routeState,
|
||||
getState: getCurrentState,
|
||||
setState: setCurrentState,
|
||||
getKey,
|
||||
@@ -95,7 +95,7 @@ export default function SceneView<
|
||||
addOptionsGetter,
|
||||
}),
|
||||
[
|
||||
route.state,
|
||||
routeState,
|
||||
getCurrentState,
|
||||
setCurrentState,
|
||||
getKey,
|
||||
|
||||
@@ -64,7 +64,58 @@ it('gets navigate action from state for top-level screen', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state for top-level screen with 2 screens', () => {
|
||||
it('gets reset action from state with 1 route with key at root', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
key: 'test',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
key: 'test',
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getActionFromState(state)).toEqual({
|
||||
payload: {
|
||||
routes: [
|
||||
{
|
||||
key: 'test',
|
||||
name: 'foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
state: {
|
||||
routes: [
|
||||
{ key: 'test', name: 'qux', params: { author: 'jane' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'RESET',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets reset action from state for top-level screen with 2 screens', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
@@ -95,37 +146,7 @@ it('gets navigate action from state for top-level screen with 2 screens', () =>
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state for top-level screen with 2 screens with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
initialRouteName: 'foo',
|
||||
screens: {
|
||||
bar: 'bar',
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
name: 'bar',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state for top-level screen with more than 2 screens with config', () => {
|
||||
it('gets reset action from state for top-level screen with more than 2 screens with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
@@ -165,6 +186,76 @@ it('gets navigate action from state for top-level screen with more than 2 screen
|
||||
});
|
||||
});
|
||||
|
||||
it('gets reset action from state for top-level screen with 2 screens with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
key: 'test',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
initialRouteName: 'foo',
|
||||
screens: {
|
||||
bar: 'bar',
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
key: 'test',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'RESET',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state for top-level screen with 2 screens with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
params: { answer: 42 },
|
||||
},
|
||||
{
|
||||
name: 'bar',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
initialRouteName: 'foo',
|
||||
screens: {
|
||||
bar: 'bar',
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
name: 'bar',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state for top-level screen with more than 2 screens with config with lower index', () => {
|
||||
const state = {
|
||||
index: 1,
|
||||
@@ -550,7 +641,7 @@ it('gets navigate action from state with 2 screens without initial route and wit
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state with 2 screens including route with key and with config', () => {
|
||||
it('gets navigate action from state with 2 screens including route with key on initial route and with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
@@ -615,6 +706,75 @@ it('gets navigate action from state with 2 screens including route with key and
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state with 2 screens including route with key on 2nd route and with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
name: 'foo',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'bar',
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: { author: 'jane' },
|
||||
},
|
||||
{
|
||||
key: 'test',
|
||||
name: 'quz',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = {
|
||||
screens: {
|
||||
foo: {
|
||||
initialRouteName: 'bar',
|
||||
screens: {
|
||||
bar: {
|
||||
initialRouteName: 'qux',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(getActionFromState(state, config)).toEqual({
|
||||
payload: {
|
||||
name: 'foo',
|
||||
params: {
|
||||
initial: true,
|
||||
screen: 'bar',
|
||||
params: {
|
||||
state: {
|
||||
routes: [
|
||||
{
|
||||
name: 'qux',
|
||||
params: {
|
||||
author: 'jane',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'test',
|
||||
name: 'quz',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
type: 'NAVIGATE',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets navigate action from state with more than 2 screens and with config', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
@@ -743,7 +903,7 @@ it("doesn't return action if no routes are provided'", () => {
|
||||
expect(getActionFromState({ routes: [] })).toBe(undefined);
|
||||
});
|
||||
|
||||
it('gets reset action from state', () => {
|
||||
it('gets undefined action from state', () => {
|
||||
const state = {
|
||||
routes: [
|
||||
{
|
||||
|
||||
@@ -747,7 +747,7 @@ it('navigates to nested child in a navigator', () => {
|
||||
act(() => navigation.current?.navigate('bar', { screen: 'bar-b' }));
|
||||
|
||||
expect(element).toMatchInlineSnapshot(
|
||||
`"[bar-b, {\\"some\\":\\"stuff\\",\\"test\\":42}]"`
|
||||
`"[bar-b, {\\"some\\":\\"stuff\\",\\"test\\":42,\\"whoa\\":\\"test\\"}]"`
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1178,3 +1178,149 @@ it("prevents removing by multiple screens with 'beforeRemove' event", () => {
|
||||
type: 'stack',
|
||||
});
|
||||
});
|
||||
|
||||
it("prevents removing a child screen with 'beforeRemove' event with 'resetRoot'", () => {
|
||||
const TestNavigator = (props: any) => {
|
||||
const { state, descriptors } = useNavigationBuilder(StackRouter, props);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{state.routes.map((route) => descriptors[route.key].render())}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const onBeforeRemove = jest.fn();
|
||||
|
||||
let shouldPrevent = true;
|
||||
let shouldContinue = false;
|
||||
|
||||
const TestScreen = (props: any) => {
|
||||
React.useEffect(
|
||||
() =>
|
||||
props.navigation.addListener('beforeRemove', (e: any) => {
|
||||
onBeforeRemove();
|
||||
|
||||
if (shouldPrevent) {
|
||||
e.preventDefault();
|
||||
|
||||
if (shouldContinue) {
|
||||
props.navigation.dispatch(e.data.action);
|
||||
}
|
||||
}
|
||||
}),
|
||||
[props.navigation]
|
||||
);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const ref = React.createRef<NavigationContainerRef>();
|
||||
|
||||
const element = (
|
||||
<BaseNavigationContainer ref={ref} onStateChange={onStateChange}>
|
||||
<TestNavigator>
|
||||
<Screen name="foo">{() => null}</Screen>
|
||||
<Screen name="bar">{() => null}</Screen>
|
||||
<Screen name="baz">
|
||||
{() => (
|
||||
<TestNavigator>
|
||||
<Screen name="qux" component={TestScreen} />
|
||||
<Screen name="lex">{() => null}</Screen>
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</BaseNavigationContainer>
|
||||
);
|
||||
|
||||
render(element);
|
||||
|
||||
act(() => ref.current?.navigate('baz'));
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).toBeCalledWith({
|
||||
index: 1,
|
||||
key: 'stack-2',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'foo-3', name: 'foo' },
|
||||
{
|
||||
key: 'baz-4',
|
||||
name: 'baz',
|
||||
state: {
|
||||
index: 0,
|
||||
key: 'stack-6',
|
||||
routeNames: ['qux', 'lex'],
|
||||
routes: [{ key: 'qux-7', name: 'qux' }],
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
},
|
||||
},
|
||||
],
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
});
|
||||
|
||||
act(() =>
|
||||
ref.current?.resetRoot({
|
||||
index: 0,
|
||||
key: 'stack-2',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [{ key: 'foo-3', name: 'foo' }],
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
})
|
||||
);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onBeforeRemove).toBeCalledTimes(1);
|
||||
|
||||
expect(ref.current?.getRootState()).toEqual({
|
||||
index: 1,
|
||||
key: 'stack-2',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'foo-3', name: 'foo' },
|
||||
{
|
||||
key: 'baz-4',
|
||||
name: 'baz',
|
||||
state: {
|
||||
index: 0,
|
||||
key: 'stack-6',
|
||||
routeNames: ['qux', 'lex'],
|
||||
routes: [{ key: 'qux-7', name: 'qux' }],
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
},
|
||||
},
|
||||
],
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
});
|
||||
|
||||
shouldPrevent = false;
|
||||
|
||||
act(() =>
|
||||
ref.current?.resetRoot({
|
||||
index: 0,
|
||||
key: 'stack-2',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [{ key: 'foo-3', name: 'foo' }],
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
})
|
||||
);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(2);
|
||||
expect(onStateChange).toBeCalledWith({
|
||||
index: 0,
|
||||
key: 'stack-2',
|
||||
routeNames: ['foo', 'bar', 'baz'],
|
||||
routes: [{ key: 'foo-3', name: 'foo' }],
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import type {
|
||||
Route,
|
||||
PartialRoute,
|
||||
ParamListBase,
|
||||
NavigationState,
|
||||
PartialState,
|
||||
CommonActions,
|
||||
} from '@react-navigation/routers';
|
||||
import type { PathConfig, PathConfigMap, NestedNavigateParams } from './types';
|
||||
import type { PathConfig, PathConfigMap, NavigatorScreenParams } from './types';
|
||||
|
||||
type ConfigItem = {
|
||||
initialRouteName?: string;
|
||||
@@ -18,7 +19,7 @@ type NavigateAction<State extends NavigationState> = {
|
||||
type: 'NAVIGATE';
|
||||
payload: {
|
||||
name: string;
|
||||
params?: NestedNavigateParams<State>;
|
||||
params?: NavigatorScreenParams<State>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -38,9 +39,11 @@ export default function getActionFromState(
|
||||
|
||||
if (
|
||||
!(
|
||||
routes.length === 1 ||
|
||||
(routes.length === 1 && routes[0].key === undefined) ||
|
||||
(routes.length === 2 &&
|
||||
routes[0].name === normalizedConfig?.initialRouteName)
|
||||
routes[0].key === undefined &&
|
||||
routes[0].name === normalizedConfig?.initialRouteName &&
|
||||
routes[1].key === undefined)
|
||||
)
|
||||
) {
|
||||
return {
|
||||
@@ -53,7 +56,10 @@ export default function getActionFromState(
|
||||
|
||||
let current: PartialState<NavigationState> | undefined = route?.state;
|
||||
let config: ConfigItem | undefined = normalizedConfig?.screens?.[route?.name];
|
||||
let params: NestedNavigateParams<NavigationState> = { ...route.params };
|
||||
let params = { ...route.params } as NavigatorScreenParams<
|
||||
ParamListBase,
|
||||
NavigationState
|
||||
>;
|
||||
|
||||
let payload = route ? { name: route.name, params } : undefined;
|
||||
|
||||
@@ -70,29 +76,36 @@ export default function getActionFromState(
|
||||
const route: Route<string> | PartialRoute<Route<string>> =
|
||||
routes[routes.length - 1];
|
||||
|
||||
if (routes.length === 1) {
|
||||
// Explicitly set to override existing value when merging params
|
||||
Object.assign(params, {
|
||||
initial: undefined,
|
||||
screen: undefined,
|
||||
params: undefined,
|
||||
state: undefined,
|
||||
});
|
||||
|
||||
if (routes.length === 1 && routes[0].key === undefined) {
|
||||
params.initial = true;
|
||||
params.screen = route.name;
|
||||
params.state = undefined; // Explicitly set to override existing value when merging params
|
||||
} else if (
|
||||
routes.length === 2 &&
|
||||
routes[0].key === undefined &&
|
||||
routes[0].name === config?.initialRouteName
|
||||
routes[0].name === config?.initialRouteName &&
|
||||
routes[1].key === undefined
|
||||
) {
|
||||
params.initial = false;
|
||||
params.screen = route.name;
|
||||
params.state = undefined;
|
||||
} else {
|
||||
params.initial = undefined;
|
||||
params.screen = undefined;
|
||||
params.params = undefined;
|
||||
params.state = current;
|
||||
break;
|
||||
}
|
||||
|
||||
if (route.state) {
|
||||
params.params = { ...route.params };
|
||||
params = params.params;
|
||||
params = params.params as NavigatorScreenParams<
|
||||
ParamListBase,
|
||||
NavigationState
|
||||
>;
|
||||
} else {
|
||||
params.params = route.params;
|
||||
}
|
||||
|
||||
@@ -3,11 +3,17 @@ import type {
|
||||
PartialState,
|
||||
NavigationState,
|
||||
} from '@react-navigation/routers';
|
||||
import { SUPPRESS_STATE_ACCESS_WARNING } from './useRouteCache';
|
||||
|
||||
export default function getFocusedRouteNameFromRoute(
|
||||
route: Partial<Route<string>> & { state?: PartialState<NavigationState> }
|
||||
): string | undefined {
|
||||
SUPPRESS_STATE_ACCESS_WARNING.value = true;
|
||||
|
||||
const state = route.state;
|
||||
|
||||
SUPPRESS_STATE_ACCESS_WARNING.value = false;
|
||||
|
||||
const params = route.params as { screen?: unknown } | undefined;
|
||||
|
||||
const routeName = state
|
||||
|
||||
@@ -504,19 +504,31 @@ export type TypedNavigator<
|
||||
) => null;
|
||||
};
|
||||
|
||||
export type NestedNavigateParams<State extends NavigationState> =
|
||||
| {
|
||||
screen?: string;
|
||||
params?: object;
|
||||
initial?: boolean;
|
||||
state?: never;
|
||||
}
|
||||
export type NavigatorScreenParams<
|
||||
ParamList,
|
||||
State extends NavigationState = NavigationState
|
||||
> =
|
||||
| {
|
||||
screen?: never;
|
||||
params?: never;
|
||||
initial?: never;
|
||||
state?: PartialState<State> | State;
|
||||
};
|
||||
state: PartialState<State> | State | undefined;
|
||||
}
|
||||
| {
|
||||
[RouteName in keyof ParamList]: undefined extends ParamList[RouteName]
|
||||
? {
|
||||
screen: RouteName;
|
||||
params?: ParamList[RouteName];
|
||||
initial?: boolean;
|
||||
state?: never;
|
||||
}
|
||||
: {
|
||||
screen: RouteName;
|
||||
params: ParamList[RouteName];
|
||||
initial?: boolean;
|
||||
state?: never;
|
||||
};
|
||||
}[keyof ParamList];
|
||||
|
||||
export type PathConfig = {
|
||||
path?: string;
|
||||
|
||||
@@ -12,6 +12,7 @@ import NavigationBuilderContext, {
|
||||
} from './NavigationBuilderContext';
|
||||
import type { NavigationEventEmitter } from './useEventEmitter';
|
||||
import useNavigationCache from './useNavigationCache';
|
||||
import useRouteCache from './useRouteCache';
|
||||
import NavigationContext from './NavigationContext';
|
||||
import NavigationRouteContext from './NavigationRouteContext';
|
||||
import type {
|
||||
@@ -113,9 +114,11 @@ export default function useDescriptors<
|
||||
emitter,
|
||||
});
|
||||
|
||||
return state.routes.reduce<
|
||||
const routes = useRouteCache(state.routes);
|
||||
|
||||
return routes.reduce<
|
||||
Record<string, Descriptor<ParamListBase, string, State, ScreenOptions>>
|
||||
>((acc, route) => {
|
||||
>((acc, route, i) => {
|
||||
const screen = screens[route.name];
|
||||
const navigation = navigations[route.key];
|
||||
|
||||
@@ -151,6 +154,7 @@ export default function useDescriptors<
|
||||
navigation={navigation}
|
||||
route={route}
|
||||
screen={screen}
|
||||
routeState={state.routes[i].state}
|
||||
getState={getState}
|
||||
setState={setState}
|
||||
options={routeOptions}
|
||||
|
||||
@@ -1,32 +1,42 @@
|
||||
import * as React from 'react';
|
||||
import { useSubscription } from 'use-subscription';
|
||||
import { useState } from 'react';
|
||||
import useNavigation from './useNavigation';
|
||||
|
||||
/**
|
||||
* Hook to get the current focus state of the screen. Returns a `true` if screen is focused, otherwise `false`.
|
||||
* This can be used if a component needs to render something based on the focus state.
|
||||
* It uses `use-subscription` under the hood for safer use in concurrent mode.
|
||||
*/
|
||||
export default function useIsFocused(): boolean {
|
||||
const navigation = useNavigation();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const getCurrentValue = React.useCallback(navigation.isFocused, [navigation]);
|
||||
const subscribe = React.useCallback(
|
||||
(callback: () => void) => {
|
||||
const unsubscribeFocus = navigation.addListener('focus', callback);
|
||||
const [isFocused, setIsFocused] = useState(navigation.isFocused);
|
||||
|
||||
const unsubscribeBlur = navigation.addListener('blur', callback);
|
||||
const valueToReturn = navigation.isFocused();
|
||||
|
||||
return () => {
|
||||
unsubscribeFocus();
|
||||
unsubscribeBlur();
|
||||
};
|
||||
},
|
||||
[navigation]
|
||||
);
|
||||
if (isFocused !== valueToReturn) {
|
||||
// If the value has changed since the last render, we need to update it.
|
||||
// This could happen if we missed an update from the event listeners during re-render.
|
||||
// React will process this update immediately, so the old subscription value won't be committed.
|
||||
// It is still nice to avoid returning a mismatched value though, so let's override the return value.
|
||||
// This is the same logic as in https://github.com/facebook/react/tree/master/packages/use-subscription
|
||||
setIsFocused(valueToReturn);
|
||||
}
|
||||
|
||||
return useSubscription({
|
||||
getCurrentValue,
|
||||
subscribe,
|
||||
});
|
||||
React.useEffect(() => {
|
||||
const unsubscribeFocus = navigation.addListener('focus', () =>
|
||||
setIsFocused(true)
|
||||
);
|
||||
|
||||
const unsubscribeBlur = navigation.addListener('blur', () =>
|
||||
setIsFocused(false)
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsubscribeFocus();
|
||||
unsubscribeBlur();
|
||||
};
|
||||
}, [navigation]);
|
||||
|
||||
React.useDebugValue(valueToReturn);
|
||||
|
||||
return valueToReturn;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
PrivateValueStore,
|
||||
EventMapBase,
|
||||
EventMapCore,
|
||||
NestedNavigateParams,
|
||||
NavigatorScreenParams,
|
||||
} from './types';
|
||||
|
||||
// This is to make TypeScript compiler happy
|
||||
@@ -43,7 +43,7 @@ PrivateValueStore;
|
||||
|
||||
type NavigatorRoute<State extends NavigationState> = {
|
||||
key: string;
|
||||
params?: NestedNavigateParams<State>;
|
||||
params?: NavigatorScreenParams<ParamListBase, State>;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -93,7 +93,7 @@ const getRouteConfigsFromChildren = <
|
||||
`A navigator can only contain 'Screen' components as its direct children (found '${
|
||||
// @ts-expect-error: child can be any type and we're accessing it safely, but TS doesn't understand it
|
||||
child.type?.name ? child.type.name : String(child)
|
||||
}')`
|
||||
}'). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.`
|
||||
);
|
||||
}, []);
|
||||
|
||||
@@ -356,21 +356,8 @@ export default function useNavigationBuilder<
|
||||
route.params !== previousParams)
|
||||
) {
|
||||
// FIXME: Since params are merged, `route.params.params` might contain params from an older route
|
||||
// So we need to make sure to reuse it only if:
|
||||
// - The screen is the same, so navigation happened with same params
|
||||
// - Params have actually changed
|
||||
// - It's the first navigation during initialization
|
||||
const params = (
|
||||
route.params.screen === nextState.routes[nextState.index].name
|
||||
? route.params.screen === previousParams?.screen
|
||||
: route.params.params !== previousParams?.params ||
|
||||
(route.params.initial === false && isFirstStateInitialization)
|
||||
)
|
||||
? route.params.params
|
||||
: undefined;
|
||||
|
||||
// If the route was updated with new screen name and/or params, we should navigate there
|
||||
action = CommonActions.navigate(route.params.screen, params);
|
||||
action = CommonActions.navigate(route.params.screen, route.params.params);
|
||||
}
|
||||
|
||||
// The update should be limited to current navigator only, so we call the router manually
|
||||
|
||||
@@ -90,18 +90,11 @@ export default function useOnAction({
|
||||
onDispatchAction(action, state === result);
|
||||
|
||||
if (state !== result) {
|
||||
const nextRouteKeys = (result.routes as any[]).map(
|
||||
(route: { key?: string }) => route.key
|
||||
);
|
||||
|
||||
const removedRoutes = state.routes.filter(
|
||||
(route) => !nextRouteKeys.includes(route.key)
|
||||
);
|
||||
|
||||
const isPrevented = shouldPreventRemove(
|
||||
emitter,
|
||||
beforeRemoveListeners,
|
||||
removedRoutes,
|
||||
state.routes,
|
||||
result.routes,
|
||||
action
|
||||
);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import type {
|
||||
NavigationState,
|
||||
Route,
|
||||
NavigationAction,
|
||||
} from '@react-navigation/routers';
|
||||
import NavigationBuilderContext, {
|
||||
@@ -22,11 +21,16 @@ const VISITED_ROUTE_KEYS = Symbol('VISITED_ROUTE_KEYS');
|
||||
export const shouldPreventRemove = (
|
||||
emitter: NavigationEventEmitter<EventMapCore<any>>,
|
||||
beforeRemoveListeners: Record<string, ChildBeforeRemoveListener | undefined>,
|
||||
routes: Route<string>[],
|
||||
currentRoutes: { key: string }[],
|
||||
nextRoutes: { key?: string | undefined }[],
|
||||
action: NavigationAction
|
||||
) => {
|
||||
const nextRouteKeys = nextRoutes.map((route) => route.key);
|
||||
|
||||
// Call these in reverse order so last screens handle the event first
|
||||
const reversedRoutes = [...routes].reverse();
|
||||
const removedRoutes = currentRoutes
|
||||
.filter((route) => !nextRouteKeys.includes(route.key))
|
||||
.reverse();
|
||||
|
||||
const visitedRouteKeys: Set<string> =
|
||||
// @ts-expect-error: add this property to mark that we've already emitted this action
|
||||
@@ -37,7 +41,7 @@ export const shouldPreventRemove = (
|
||||
[VISITED_ROUTE_KEYS]: visitedRouteKeys,
|
||||
};
|
||||
|
||||
for (const route of reversedRoutes) {
|
||||
for (const route of removedRoutes) {
|
||||
if (visitedRouteKeys.has(route.key)) {
|
||||
// Skip if we've already emitted this action for this screen
|
||||
continue;
|
||||
@@ -85,6 +89,7 @@ export default function useOnPreventRemove({
|
||||
emitter,
|
||||
beforeRemoveListeners,
|
||||
state.routes,
|
||||
[],
|
||||
action
|
||||
);
|
||||
});
|
||||
|
||||
61
packages/core/src/useRouteCache.tsx
Normal file
61
packages/core/src/useRouteCache.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import * as React from 'react';
|
||||
import type {
|
||||
ParamListBase,
|
||||
NavigationState,
|
||||
Route,
|
||||
} from '@react-navigation/routers';
|
||||
import type { RouteProp } from './types';
|
||||
|
||||
type RouteCache = Map<Route<string>, RouteProp<ParamListBase, string>>;
|
||||
|
||||
/**
|
||||
* Utilites such as `getFocusedRouteNameFromRoute` need to access state.
|
||||
* So we need a way to suppress the warning for those use cases.
|
||||
* This is fine since they are internal utilities and this is not public API.
|
||||
*/
|
||||
export const SUPPRESS_STATE_ACCESS_WARNING = { value: false };
|
||||
|
||||
/**
|
||||
* Hook to cache route props for each screen in the navigator.
|
||||
* This lets add warnings and modifications to the route object but keep references between renders.
|
||||
*/
|
||||
export default function useRouteCache<State extends NavigationState>(
|
||||
routes: State['routes']
|
||||
) {
|
||||
// Cache object which holds route objects for each screen
|
||||
const cache = React.useMemo(() => ({ current: new Map() as RouteCache }), []);
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
// We don't want the overhead of creating extra maps every render in prod
|
||||
return routes;
|
||||
}
|
||||
|
||||
cache.current = routes.reduce((acc, route) => {
|
||||
const previous = cache.current.get(route);
|
||||
|
||||
if (previous) {
|
||||
// If a cached route object already exists, reuse it
|
||||
acc.set(route, previous);
|
||||
} else {
|
||||
const proxy = { ...route };
|
||||
|
||||
Object.defineProperty(proxy, 'state', {
|
||||
get() {
|
||||
if (!SUPPRESS_STATE_ACCESS_WARNING.value) {
|
||||
console.warn(
|
||||
"Accessing the 'state' property of the 'route' object is not supported. If you want to get the focused route name, use the 'getFocusedRouteNameFromRoute' helper instead: https://reactnavigation.org/docs/screen-options-resolution/#setting-parent-screen-options-based-on-child-navigators-state"
|
||||
);
|
||||
}
|
||||
|
||||
return route.state;
|
||||
},
|
||||
});
|
||||
|
||||
acc.set(route, proxy);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, new Map() as RouteCache);
|
||||
|
||||
return Array.from(cache.current.values());
|
||||
}
|
||||
@@ -68,5 +68,7 @@ export default function useSyncState<T>(initialState?: (() => T) | T) {
|
||||
|
||||
const state = stateRef.current;
|
||||
|
||||
React.useDebugValue(state);
|
||||
|
||||
return [state, getState, setState, scheduleUpdate, flushUpdates] as const;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.1.15](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.14...@react-navigation/devtools@5.1.15) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.14](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.13...@react-navigation/devtools@5.1.14) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.13](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.12...@react-navigation/devtools@5.1.13) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.12](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.11...@react-navigation/devtools@5.1.12) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.1.11](https://github.com/react-navigation/react-navigation/compare/@react-navigation/devtools@5.1.10...@react-navigation/devtools@5.1.11) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/devtools
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/devtools",
|
||||
"description": "Developer tools for React Navigation",
|
||||
"version": "5.1.11",
|
||||
"version": "5.1.15",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-native",
|
||||
@@ -36,7 +36,7 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/core": "^5.13.3",
|
||||
"@react-navigation/core": "^5.14.1",
|
||||
"deep-equal": "^2.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.10.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.10.6...@react-navigation/drawer@5.10.7) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.10.5...@react-navigation/drawer@5.10.6) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.10.4...@react-navigation/drawer@5.10.5) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.10.3...@react-navigation/drawer@5.10.4) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.10.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/drawer@5.10.2...@react-navigation/drawer@5.10.3) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/drawer
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/drawer",
|
||||
"description": "Drawer navigator component with animated transitions and gesturess",
|
||||
"version": "5.10.3",
|
||||
"version": "5.10.7",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -46,7 +46,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"@react-navigation/native": "^5.8.3",
|
||||
"@react-navigation/native": "^5.8.7",
|
||||
"@testing-library/react-native": "^7.1.0",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-native": "^0.63.30",
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.3.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.6...@react-navigation/material-bottom-tabs@5.3.7) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.5...@react-navigation/material-bottom-tabs@5.3.6) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.4...@react-navigation/material-bottom-tabs@5.3.5) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.3...@react-navigation/material-bottom-tabs@5.3.4) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-bottom-tabs@5.3.2...@react-navigation/material-bottom-tabs@5.3.3) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-bottom-tabs
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/material-bottom-tabs",
|
||||
"description": "Integration for bottom navigation component from react-native-paper",
|
||||
"version": "5.3.3",
|
||||
"version": "5.3.7",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -42,7 +42,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"@react-navigation/native": "^5.8.3",
|
||||
"@react-navigation/native": "^5.8.7",
|
||||
"@testing-library/react-native": "^7.1.0",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-native": "^0.63.30",
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.3.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.6...@react-navigation/material-top-tabs@5.3.7) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.5...@react-navigation/material-top-tabs@5.3.6) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.4...@react-navigation/material-top-tabs@5.3.5) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.3...@react-navigation/material-top-tabs@5.3.4) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.3.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/material-top-tabs@5.3.2...@react-navigation/material-top-tabs@5.3.3) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/material-top-tabs
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/material-top-tabs",
|
||||
"description": "Integration for the animated tab view component from react-native-tab-view",
|
||||
"version": "5.3.3",
|
||||
"version": "5.3.7",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -45,7 +45,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"@react-navigation/native": "^5.8.3",
|
||||
"@react-navigation/native": "^5.8.7",
|
||||
"@testing-library/react-native": "^7.1.0",
|
||||
"@types/react": "^16.9.53",
|
||||
"@types/react-native": "^0.63.30",
|
||||
|
||||
@@ -3,6 +3,41 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.8.7](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.6...@react-navigation/native@5.8.7) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.8.6](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.5...@react-navigation/native@5.8.6) (2020-11-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ignore any errors from deep linking ([4c2379c](https://github.com/react-navigation/react-navigation/commit/4c2379cec1e661aa132002fd1c50909ea64cb983))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.8.5](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.4...@react-navigation/native@5.8.5) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.8.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.3...@react-navigation/native@5.8.4) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/native
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.8.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/native@5.8.2...@react-navigation/native@5.8.3) (2020-11-03)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/native",
|
||||
"description": "React Native integration for React Navigation",
|
||||
"version": "5.8.3",
|
||||
"version": "5.8.7",
|
||||
"keywords": [
|
||||
"react-native",
|
||||
"react-navigation",
|
||||
@@ -37,7 +37,7 @@
|
||||
"clean": "del lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-navigation/core": "^5.13.3",
|
||||
"@react-navigation/core": "^5.14.1",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"nanoid": "^3.1.15"
|
||||
},
|
||||
|
||||
@@ -137,7 +137,15 @@ export default function useLinking(
|
||||
const action = getActionFromState(state, configRef.current);
|
||||
|
||||
if (action !== undefined) {
|
||||
navigation.dispatch(action);
|
||||
try {
|
||||
navigation.dispatch(action);
|
||||
} catch (e) {
|
||||
// Ignore any errors from deep linking.
|
||||
// This could happen in case of malformed links, navigation object not being initialized etc.
|
||||
console.warn(
|
||||
`An error occurred when trying to handle the link '${path}': ${e.message}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
navigation.resetRoot(state);
|
||||
}
|
||||
|
||||
@@ -415,7 +415,15 @@ export default function useLinking(
|
||||
const action = getActionFromState(state, configRef.current);
|
||||
|
||||
if (action !== undefined) {
|
||||
navigation.dispatch(action);
|
||||
try {
|
||||
navigation.dispatch(action);
|
||||
} catch (e) {
|
||||
// Ignore any errors from deep linking.
|
||||
// This could happen in case of malformed links, navigation object not being initialized etc.
|
||||
console.warn(
|
||||
`An error occurred when trying to handle the link '${path}': ${e.message}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
navigation.resetRoot(state);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,25 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.6.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@5.6.0...@react-navigation/routers@5.6.1) (2020-11-08)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/routers
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.6.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@5.5.1...@react-navigation/routers@5.6.0) (2020-11-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add a merge option to navigate to control merging params ([9beca3a](https://github.com/react-navigation/react-navigation/commit/9beca3a8027c6e2135dbef2abb8eede6b0b4bc44))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.5.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/routers@5.5.0...@react-navigation/routers@5.5.1) (2020-10-28)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/routers",
|
||||
"description": "Routers to help build custom navigators",
|
||||
"version": "5.5.1",
|
||||
"version": "5.6.1",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-native",
|
||||
|
||||
@@ -16,14 +16,14 @@ export type Action =
|
||||
| {
|
||||
type: 'NAVIGATE';
|
||||
payload:
|
||||
| { key: string; name?: undefined; params?: object }
|
||||
| { name: string; key?: string; params?: object };
|
||||
| { key: string; name?: undefined; params?: object; merge?: boolean }
|
||||
| { name: string; key?: string; params?: object; merge?: boolean };
|
||||
source?: string;
|
||||
target?: string;
|
||||
}
|
||||
| {
|
||||
type: 'RESET';
|
||||
payload: ResetState;
|
||||
payload: ResetState | undefined;
|
||||
source?: string;
|
||||
target?: string;
|
||||
}
|
||||
@@ -62,7 +62,7 @@ export function navigate(...args: any): Action {
|
||||
}
|
||||
}
|
||||
|
||||
export function reset(state: ResetState): Action {
|
||||
export function reset(state: ResetState | undefined): Action {
|
||||
return { type: 'RESET', payload: state };
|
||||
}
|
||||
|
||||
|
||||
@@ -397,19 +397,34 @@ export default function StackRouter(options: StackRouterOptions) {
|
||||
);
|
||||
}
|
||||
|
||||
const route = state.routes[index];
|
||||
|
||||
let params;
|
||||
|
||||
if (action.payload.merge === false) {
|
||||
params =
|
||||
routeParamList[route.name] !== undefined
|
||||
? {
|
||||
...routeParamList[route.name],
|
||||
...action.payload.params,
|
||||
}
|
||||
: action.payload.params;
|
||||
} else {
|
||||
params = action.payload.params
|
||||
? {
|
||||
...route.params,
|
||||
...action.payload.params,
|
||||
}
|
||||
: route.params;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
index,
|
||||
routes: [
|
||||
...state.routes.slice(0, index),
|
||||
action.payload.params !== undefined
|
||||
? {
|
||||
...state.routes[index],
|
||||
params: {
|
||||
...state.routes[index].params,
|
||||
...action.payload.params,
|
||||
},
|
||||
}
|
||||
params !== route.params
|
||||
? { ...route, params }
|
||||
: state.routes[index],
|
||||
],
|
||||
};
|
||||
|
||||
@@ -271,7 +271,7 @@ export default function TabRouter({
|
||||
return changeIndex(state, index, backBehavior, initialRouteName);
|
||||
},
|
||||
|
||||
getStateForAction(state, action) {
|
||||
getStateForAction(state, action, { routeParamList }) {
|
||||
switch (action.type) {
|
||||
case 'JUMP_TO':
|
||||
case 'NAVIGATE': {
|
||||
@@ -296,17 +296,37 @@ export default function TabRouter({
|
||||
...state,
|
||||
routes:
|
||||
action.payload.params !== undefined
|
||||
? state.routes.map((route, i) =>
|
||||
i === index
|
||||
? {
|
||||
...route,
|
||||
params: {
|
||||
? state.routes.map((route, i) => {
|
||||
if (i !== index) {
|
||||
return route;
|
||||
}
|
||||
|
||||
let params;
|
||||
|
||||
if (
|
||||
action.type === 'NAVIGATE' &&
|
||||
action.payload.merge === false
|
||||
) {
|
||||
params =
|
||||
routeParamList[route.name] !== undefined
|
||||
? {
|
||||
...routeParamList[route.name],
|
||||
...action.payload.params,
|
||||
}
|
||||
: action.payload.params;
|
||||
} else {
|
||||
params = action.payload.params
|
||||
? {
|
||||
...route.params,
|
||||
...action.payload.params,
|
||||
},
|
||||
}
|
||||
: route
|
||||
)
|
||||
}
|
||||
: route.params;
|
||||
}
|
||||
|
||||
return params !== route.params
|
||||
? { ...route, params }
|
||||
: route;
|
||||
})
|
||||
: state.routes,
|
||||
},
|
||||
index,
|
||||
|
||||
@@ -1074,3 +1074,115 @@ it('merges params on navigate to an existing screen', () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't merge params on navigate to an existing screen if merge: false", () => {
|
||||
const router = StackRouter({});
|
||||
const options = {
|
||||
routeNames: ['baz', 'bar', 'qux'],
|
||||
routeParamList: {
|
||||
baz: { foo: 12 },
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
router.getStateForAction(
|
||||
{
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
key: 'root',
|
||||
index: 2,
|
||||
routeNames: ['baz', 'bar', 'qux'],
|
||||
routes: [
|
||||
{ key: 'baz', name: 'baz' },
|
||||
{ key: 'bar', name: 'bar', params: { answer: 42 } },
|
||||
{ key: 'qux', name: 'qux' },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'NAVIGATE',
|
||||
payload: {
|
||||
name: 'bar',
|
||||
merge: false,
|
||||
},
|
||||
},
|
||||
options
|
||||
)
|
||||
).toEqual({
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
key: 'root',
|
||||
index: 1,
|
||||
routeNames: ['baz', 'bar', 'qux'],
|
||||
routes: [
|
||||
{ key: 'baz', name: 'baz' },
|
||||
{ key: 'bar', name: 'bar' },
|
||||
],
|
||||
});
|
||||
|
||||
expect(
|
||||
router.getStateForAction(
|
||||
{
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
key: 'root',
|
||||
index: 1,
|
||||
routeNames: ['baz', 'bar', 'qux'],
|
||||
routes: [
|
||||
{ key: 'baz', name: 'baz' },
|
||||
{ key: 'bar', name: 'bar', params: { answer: 42 } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'NAVIGATE',
|
||||
payload: {
|
||||
name: 'bar',
|
||||
params: { fruit: 'orange' },
|
||||
merge: false,
|
||||
},
|
||||
},
|
||||
options
|
||||
)
|
||||
).toEqual({
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
key: 'root',
|
||||
index: 1,
|
||||
routeNames: ['baz', 'bar', 'qux'],
|
||||
routes: [
|
||||
{ key: 'baz', name: 'baz' },
|
||||
{ key: 'bar', name: 'bar', params: { fruit: 'orange' } },
|
||||
],
|
||||
});
|
||||
|
||||
expect(
|
||||
router.getStateForAction(
|
||||
{
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
key: 'root',
|
||||
index: 1,
|
||||
routeNames: ['baz', 'bar', 'qux'],
|
||||
routes: [
|
||||
{ key: 'baz', name: 'baz', params: { test: 99 } },
|
||||
{ key: 'bar', name: 'bar', params: { answer: 42 } },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'NAVIGATE',
|
||||
payload: {
|
||||
name: 'baz',
|
||||
params: { color: 'black' },
|
||||
merge: false,
|
||||
},
|
||||
},
|
||||
options
|
||||
)
|
||||
).toEqual({
|
||||
stale: false,
|
||||
type: 'stack',
|
||||
key: 'root',
|
||||
index: 0,
|
||||
routeNames: ['baz', 'bar', 'qux'],
|
||||
routes: [{ key: 'baz', name: 'baz', params: { foo: 12, color: 'black' } }],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1186,6 +1186,141 @@ it('merges params on navigate to an existing screen', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't merge params on navigate to an existing screen if merge: false", () => {
|
||||
const router = TabRouter({});
|
||||
const options = {
|
||||
routeNames: ['baz', 'bar', 'qux'],
|
||||
routeParamList: {
|
||||
qux: { color: 'indigo' },
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
router.getStateForAction(
|
||||
{
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
key: 'root',
|
||||
index: 1,
|
||||
routeNames: ['baz', 'bar', 'qux'],
|
||||
routes: [
|
||||
{ key: 'baz', name: 'baz' },
|
||||
{ key: 'bar', name: 'bar', params: { answer: 42 } },
|
||||
{ key: 'qux', name: 'qux' },
|
||||
],
|
||||
history: [{ type: 'route', key: 'baz' }],
|
||||
},
|
||||
{
|
||||
type: 'NAVIGATE',
|
||||
payload: {
|
||||
name: 'bar',
|
||||
merge: false,
|
||||
},
|
||||
},
|
||||
options
|
||||
)
|
||||
).toEqual({
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
key: 'root',
|
||||
index: 1,
|
||||
routeNames: ['baz', 'bar', 'qux'],
|
||||
routes: [
|
||||
{ key: 'baz', name: 'baz' },
|
||||
{ key: 'bar', name: 'bar', params: { answer: 42 } },
|
||||
{ key: 'qux', name: 'qux' },
|
||||
],
|
||||
history: [
|
||||
{ type: 'route', key: 'baz' },
|
||||
{ type: 'route', key: 'bar' },
|
||||
],
|
||||
});
|
||||
|
||||
expect(
|
||||
router.getStateForAction(
|
||||
{
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
key: 'root',
|
||||
index: 1,
|
||||
routeNames: ['baz', 'bar', 'qux'],
|
||||
routes: [
|
||||
{ key: 'baz', name: 'baz' },
|
||||
{ key: 'bar', name: 'bar' },
|
||||
{ key: 'qux', name: 'qux' },
|
||||
],
|
||||
history: [{ type: 'route', key: 'baz' }],
|
||||
},
|
||||
{
|
||||
type: 'NAVIGATE',
|
||||
payload: {
|
||||
name: 'bar',
|
||||
params: { fruit: 'orange' },
|
||||
merge: false,
|
||||
},
|
||||
},
|
||||
options
|
||||
)
|
||||
).toEqual({
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
key: 'root',
|
||||
index: 1,
|
||||
routeNames: ['baz', 'bar', 'qux'],
|
||||
routes: [
|
||||
{ key: 'baz', name: 'baz' },
|
||||
{ key: 'bar', name: 'bar', params: { fruit: 'orange' } },
|
||||
{ key: 'qux', name: 'qux' },
|
||||
],
|
||||
history: [
|
||||
{ type: 'route', key: 'baz' },
|
||||
{ type: 'route', key: 'bar' },
|
||||
],
|
||||
});
|
||||
|
||||
expect(
|
||||
router.getStateForAction(
|
||||
{
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
key: 'root',
|
||||
index: 1,
|
||||
routeNames: ['baz', 'bar', 'qux'],
|
||||
routes: [
|
||||
{ key: 'baz', name: 'baz' },
|
||||
{ key: 'bar', name: 'bar' },
|
||||
{ key: 'qux', name: 'qux' },
|
||||
],
|
||||
history: [{ type: 'route', key: 'baz' }],
|
||||
},
|
||||
{
|
||||
type: 'NAVIGATE',
|
||||
payload: {
|
||||
name: 'qux',
|
||||
params: { test: 12 },
|
||||
merge: false,
|
||||
},
|
||||
},
|
||||
options
|
||||
)
|
||||
).toEqual({
|
||||
stale: false,
|
||||
type: 'tab',
|
||||
key: 'root',
|
||||
index: 2,
|
||||
routeNames: ['baz', 'bar', 'qux'],
|
||||
routes: [
|
||||
{ key: 'baz', name: 'baz' },
|
||||
{ key: 'bar', name: 'bar' },
|
||||
{ key: 'qux', name: 'qux', params: { color: 'indigo', test: 12 } },
|
||||
],
|
||||
history: [
|
||||
{ type: 'route', key: 'baz' },
|
||||
{ type: 'route', key: 'qux' },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('merges params on jump to an existing screen', () => {
|
||||
const router = TabRouter({});
|
||||
const options = {
|
||||
|
||||
@@ -56,12 +56,11 @@ export type PartialRoute<R extends Route<string>> = Omit<R, 'key'> & {
|
||||
};
|
||||
|
||||
export type PartialState<State extends NavigationState> = Partial<
|
||||
Omit<State, 'stale' | 'type' | 'key' | 'routes' | 'routeNames'>
|
||||
Omit<State, 'stale' | 'routes'>
|
||||
> &
|
||||
Readonly<{
|
||||
stale?: true;
|
||||
type?: string;
|
||||
routes: PartialRoute<Route<string>>[];
|
||||
routes: PartialRoute<Route<State['routeNames'][number]>>[];
|
||||
}>;
|
||||
|
||||
export type Route<
|
||||
|
||||
@@ -3,6 +3,45 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [5.12.4](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.12.3...@react-navigation/stack@5.12.4) (2020-11-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* don't hide child header automatically in stack ([8f0efc8](https://github.com/react-navigation/react-navigation/commit/8f0efc8db534297a95ea8a2bcb6d2e387c1fea53))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.12.3](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.12.2...@react-navigation/stack@5.12.3) (2020-11-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* android textinput gets blurred after navigating back ([1169ed0](https://github.com/react-navigation/react-navigation/commit/1169ed0946df609cb7e5c52c4bdda0aa91b5737f))
|
||||
* disable react-native-screens on iOS for older versions ([ce7d20e](https://github.com/react-navigation/react-navigation/commit/ce7d20e3366415b07a537e01ee0b17ce7e72cad6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.12.2](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.12.1...@react-navigation/stack@5.12.2) (2020-11-04)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [5.12.1](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.12.0...@react-navigation/stack@5.12.1) (2020-11-03)
|
||||
|
||||
**Note:** Version bump only for package @react-navigation/stack
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [5.12.0](https://github.com/react-navigation/react-navigation/compare/@react-navigation/stack@5.11.1...@react-navigation/stack@5.12.0) (2020-11-03)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@react-navigation/stack",
|
||||
"description": "Stack navigator component for iOS and Android with animated transitions and gestures",
|
||||
"version": "5.12.0",
|
||||
"version": "5.12.4",
|
||||
"keywords": [
|
||||
"react-native-component",
|
||||
"react-component",
|
||||
@@ -46,7 +46,7 @@
|
||||
"devDependencies": {
|
||||
"@react-native-community/bob": "^0.16.2",
|
||||
"@react-native-community/masked-view": "^0.1.10",
|
||||
"@react-navigation/native": "^5.8.3",
|
||||
"@react-navigation/native": "^5.8.7",
|
||||
"@testing-library/react-native": "^7.1.0",
|
||||
"@types/color": "^3.0.1",
|
||||
"@types/react": "^16.9.53",
|
||||
|
||||
@@ -369,7 +369,7 @@ export type StackNavigationConfig = {
|
||||
/**
|
||||
* Whether inactive screens should be detached from the view hierarchy to save memory.
|
||||
* Make sure to call `enableScreens` from `react-native-screens` to make it work.
|
||||
* Defaults to `true`.
|
||||
* Defaults to `true` on Android, depends on the version of `react-native-screens` on iOS.
|
||||
*/
|
||||
detachInactiveScreens?: boolean;
|
||||
};
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
forNoAnimation,
|
||||
forSlideRight,
|
||||
} from '../../TransitionConfigs/HeaderStyleInterpolators';
|
||||
import HeaderShownContext from '../../utils/HeaderShownContext';
|
||||
import PreviousSceneContext from '../../utils/PreviousSceneContext';
|
||||
import type {
|
||||
Layout,
|
||||
@@ -56,7 +55,6 @@ export default function HeaderContainer({
|
||||
style,
|
||||
}: Props) {
|
||||
const focusedRoute = getFocusedRoute();
|
||||
const isParentHeaderShown = React.useContext(HeaderShownContext);
|
||||
const parentPreviousScene = React.useContext(PreviousSceneContext);
|
||||
|
||||
return (
|
||||
@@ -66,11 +64,8 @@ export default function HeaderContainer({
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
header,
|
||||
headerShown = isParentHeaderShown === false,
|
||||
headerTransparent,
|
||||
} = scene.descriptor.options || {};
|
||||
const { header, headerShown = true, headerTransparent } =
|
||||
scene.descriptor.options || {};
|
||||
|
||||
if (!headerShown) {
|
||||
return null;
|
||||
@@ -85,11 +80,10 @@ export default function HeaderContainer({
|
||||
const previousScene = self[i - 1];
|
||||
const nextScene = self[i + 1];
|
||||
|
||||
const {
|
||||
headerShown: previousHeaderShown = isParentHeaderShown === false,
|
||||
} = previousScene?.descriptor.options || {};
|
||||
const { headerShown: previousHeaderShown = true } =
|
||||
previousScene?.descriptor.options || {};
|
||||
|
||||
const { headerShown: nextHeaderShown = isParentHeaderShown === false } =
|
||||
const { headerShown: nextHeaderShown = true } =
|
||||
nextScene?.descriptor.options || {};
|
||||
|
||||
const isHeaderStatic =
|
||||
|
||||
@@ -58,10 +58,12 @@ export default class KeyboardManager extends React.Component<Props> {
|
||||
|
||||
const input = this.previouslyFocusedTextInput;
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
Keyboard.dismiss();
|
||||
} else if (input) {
|
||||
TextInput.State.blurTextInput(input);
|
||||
if (input) {
|
||||
if (Platform.OS === 'android') {
|
||||
Keyboard.dismiss();
|
||||
} else {
|
||||
TextInput.State.blurTextInput(input);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup the ID on successful page change
|
||||
|
||||
@@ -396,8 +396,9 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
onGestureStart,
|
||||
onGestureEnd,
|
||||
onGestureCancel,
|
||||
// Enable on new versions of screens or for non modals on older versions
|
||||
detachInactiveScreens = shouldUseActivityState || mode !== 'modal',
|
||||
// Enable on new versions of `react-native-screens`
|
||||
// On older versions of `react-native-screens`, there's an issue with screens not being responsive to user interaction.
|
||||
detachInactiveScreens = shouldUseActivityState,
|
||||
} = this.props;
|
||||
|
||||
const { scenes, layout, gestures, headerHeights } = this.state;
|
||||
@@ -446,10 +447,7 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
? this.state.scenes.slice(-2).some((scene) => {
|
||||
const { descriptor } = scene;
|
||||
const options = descriptor ? descriptor.options : {};
|
||||
const {
|
||||
headerTransparent,
|
||||
headerShown = isParentHeaderShown === false,
|
||||
} = options;
|
||||
const { headerTransparent, headerShown = true } = options;
|
||||
|
||||
if (headerTransparent || headerShown === false) {
|
||||
return true;
|
||||
@@ -541,7 +539,7 @@ export default class CardStack extends React.Component<Props, State> {
|
||||
|
||||
const {
|
||||
safeAreaInsets,
|
||||
headerShown = isParentHeaderShown === false,
|
||||
headerShown = true,
|
||||
headerTransparent,
|
||||
cardShadowEnabled,
|
||||
cardOverlayEnabled,
|
||||
|
||||
@@ -4034,11 +4034,6 @@
|
||||
dependencies:
|
||||
source-map "^0.6.1"
|
||||
|
||||
"@types/use-subscription@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/use-subscription/-/use-subscription-1.0.0.tgz#d146f8d834f70f50d48bd8246a481d096f11db19"
|
||||
integrity sha512-0WWZ5GUDKMXUY/1zy4Ur5/zsC0s/B+JjXfHdkvx6JgDNZzZV5eW+KKhDqsTGyqX56uh99gwGwbsKbVwkcVIKQA==
|
||||
|
||||
"@types/wait-on@*":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/wait-on/-/wait-on-4.0.0.tgz#fb6fa2854b592f7344f1dd9836b5655795510dce"
|
||||
@@ -18740,7 +18735,7 @@ urlgrey@0.4.4:
|
||||
resolved "https://registry.yarnpkg.com/urlgrey/-/urlgrey-0.4.4.tgz#892fe95960805e85519f1cd4389f2cb4cbb7652f"
|
||||
integrity sha1-iS/pWWCAXoVRnxzUOJ8stMu3ZS8=
|
||||
|
||||
use-subscription@^1.0.0, use-subscription@^1.5.0:
|
||||
use-subscription@^1.0.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.0.tgz#0df66fdf97b9a340147ad72f76fac1db6f56d240"
|
||||
integrity sha512-/FVRiB2I7NDjzWoNBYPt6YkkvleMm/lFtxj1hH6nX2TVrJ/5UTbovw9OE1efv2Zl0HoAYuTjM7zHd9OsABn5sg==
|
||||
|
||||
Reference in New Issue
Block a user