Currently when we receive a deep link after the app is rendered, it always results in a `navigate` action. While it's ok with the default configuration, it may result in incorrect behaviour when a custom `getStateForPath` function is provided and it returns a routes array different than the initial route and new route pair.
The commit changes 2 things:
1. Add ability to reset state via params of `navigate` by specifying a `state` property instead of `screen`
2. Update `getStateForAction` to return an action for reset when necessary according to the deep linking configuration
Closes#8952
The commit improves the navigation state object to have more specific types.
e.g. The `routeNames` array will now have proper type instead of `string[]`
What just randomly reading, but I think there's a typo
```
['ab', 'aba'].sort((a, b) => {
if (a.startsWith(b)) {
return -1;
}
if (b.startsWith(a)) {
return 1;
}
});
(2) ["aba", "ab"]
['ab', 'a'].sort((a, b) => {
if (a.startsWith(b)) {
return -1;
}
if (b.startsWith(a)) {
return 1;
}
});
(2) ["ab", "a"]
```
A lot of times, we want to prompt before leaving a screen if we have unsaved changes. Currently, we need to handle multiple cases to prevent this:
- Disable swipe gestures
- Override the back button in header
- Override the hardware back button on Android
This PR adds a new event which is emitted before a screen gets removed, and the developer has a chance to ask the user before closing the screen.
Example:
```js
React.useEffect(
() =>
navigation.addListener('beforeRemove', (e) => {
if (!hasUnsavedChanges) {
return;
}
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(e.data.action),
},
]
);
}),
[navigation, hasUnsavedChanges]
);
```
The PR changes a few things about linking configuration:
- Moves the configuration for screens to a screens property so that it's possible to specify other options like `initialRouteName` for the navigator at root
- The nesting in the configuration needs to strictly match the shape of the navigation tree, it can't just rely on URL's shape anymore
- If a screen is not specified in the configuration, it won't be parsed to/from the URL (this is essential to handle unmatched screens)
- Treat `path: ''` and no specified path in the same way, unless `exact` is specified
- Disallow specifying unmatched screen with old format
- Add support for `initialRouteName` at top level
- Automatically adapt old configuration to new format
The `devtools` package extracts the redux devtools extension integration to a separate package. In future we can add more tools such as flipper integration to this package.
Usage:
```js
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { useReduxDevToolsExtension } from '@react-navigation/devtools';
export default function App() {
const navigationRef = React.useRef();
useReduxDevToolsExtension(navigationRef);
return (
<NavigationContainer ref={navigationRef}>{/* ... */}</NavigationContainer>
);
}
```
Currently, to access the focused child screen, we need to do something like this:
```js
const routeName = route.state
? route.state.routes[route.state.index].name
: route.params?.screen || 'Feed';
```
However, it doesn't handle some cases, such as when `route.state` is partial. This helper will make it easier:
```js
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Feed';
```
The PR reworks history integration to better integrate with browser's history stack and supports nested navigation more reliably:
- On each navigation, save the navigation in memory and use it to reset the state when user presses back/forward
- Improve heuristic to determine if we should do a push, replace or back
This closes#8230, closes#8284 and closes#8344