Skip to content

Commit 3f2a62f

Browse files
committed
Document retaining screens in Stack
1 parent 4ba28dd commit 3f2a62f

4 files changed

Lines changed: 190 additions & 0 deletions

File tree

versioned_docs/version-8.x/native-stack-navigator.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,22 @@ Pops all of the screens in the stack except the first one and navigates to it.
13731373
navigation.popToTop();
13741374
```
13751375
1376+
#### `retain`
1377+
1378+
Marks or unmarks the current screen in the stack to be retained after it is removed from the history. The method accepts the following argument:
1379+
1380+
- `enable` - _boolean_ - Whether to retain the screen.
1381+
1382+
```js
1383+
navigation.retain(true);
1384+
```
1385+
1386+
:::warning
1387+
1388+
Retaining screens is currently not supported on Android & iOS with Native Stack.
1389+
1390+
:::
1391+
13761392
### Hooks
13771393
13781394
The native stack navigator exports the following hooks:

versioned_docs/version-8.x/stack-actions.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,3 +466,140 @@ export default function App() {
466466
return <Navigation />;
467467
}
468468
```
469+
470+
## retain
471+
472+
The `retain` action marks a route to be retained in the [navigation state](navigation-state.md) after it is removed from history.
473+
474+
It takes the following argument:
475+
476+
- `enable` - _boolean_ - Whether to retain the route. Passing `true` marks the route to be retained, while `false` unmarks it.
477+
478+
```js name="Stack actions retain" snack static2dynamic
479+
import * as React from 'react';
480+
import { View, Text } from 'react-native';
481+
import { Button } from '@react-navigation/elements';
482+
import {
483+
createStaticNavigation,
484+
useNavigation,
485+
StackActions,
486+
} from '@react-navigation/native';
487+
import { createStackNavigator } from '@react-navigation/stack';
488+
489+
function HomeScreen() {
490+
const navigation = useNavigation();
491+
492+
return (
493+
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
494+
<Text>Home!</Text>
495+
<Button
496+
onPress={() => {
497+
navigation.dispatch(StackActions.push('Profile'));
498+
}}
499+
>
500+
Push Profile on the stack
501+
</Button>
502+
</View>
503+
);
504+
}
505+
506+
function ProfileScreen() {
507+
const navigation = useNavigation();
508+
const [count, setCount] = React.useState(0);
509+
510+
return (
511+
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
512+
<Text>Profile!</Text>
513+
<Text>Count: {count}</Text>
514+
<Button onPress={() => setCount((value) => value + 1)}>
515+
Increment count
516+
</Button>
517+
<Button
518+
onPress={() => {
519+
// codeblock-focus-start
520+
navigation.dispatch(StackActions.retain(true));
521+
// codeblock-focus-end
522+
}}
523+
>
524+
Retain Profile
525+
</Button>
526+
<Button
527+
onPress={() => {
528+
navigation.dispatch(StackActions.replace('Settings'));
529+
}}
530+
>
531+
Replace with Settings
532+
</Button>
533+
<Button
534+
onPress={() => {
535+
navigation.dispatch(StackActions.retain(false));
536+
}}
537+
>
538+
Unretain Profile
539+
</Button>
540+
<Button
541+
onPress={() => {
542+
navigation.goBack();
543+
}}
544+
>
545+
Go back
546+
</Button>
547+
</View>
548+
);
549+
}
550+
551+
function SettingsScreen() {
552+
const navigation = useNavigation();
553+
554+
return (
555+
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
556+
<Text>Settings!</Text>
557+
<Button
558+
onPress={() => {
559+
navigation.navigate('Profile');
560+
}}
561+
>
562+
Navigate to retained Profile
563+
</Button>
564+
<Button onPress={() => navigation.dispatch(StackActions.popToTop())}>
565+
Pop to top
566+
</Button>
567+
</View>
568+
);
569+
}
570+
571+
const RootStack = createStackNavigator({
572+
screens: {
573+
Home: HomeScreen,
574+
Profile: ProfileScreen,
575+
Settings: SettingsScreen,
576+
},
577+
});
578+
579+
const Navigation = createStaticNavigation(RootStack);
580+
581+
export default function App() {
582+
return <Navigation />;
583+
}
584+
```
585+
586+
When a screen is marked to be retained, actions such as [`goBack`](navigation-actions.md#goback), [`pop`](#pop), [`popToTop`](#poptotop), [`replace`](#replace) etc. will remove it from history, but keep it in the navigation state. So the screen is not unmounted and stays rendered in the background, preserving its local state. Similar to [preloaded routes](navigation-actions.md#preload), it can be brought to focus with `navigate`.
587+
588+
This can be useful in various scenarios:
589+
590+
- Keeping a frequently used heavy screen in memory to avoid unmounting and remounting it for better performance when navigating back and forth.
591+
- Keeping a screen with a video or audio player rendered to enable functionality such as background playback or picture-in-picture mode when the user navigates away from the screen.
592+
593+
If a route was removed from history while being retained, `retain(false)` will remove it from the navigation state and unmount the screen. If the route is still present in history, `retain(false)` will just unmark it, and the route will be removed from the navigation state when it's removed from history.
594+
595+
By default, the action applies to the route that dispatched it. If you want to retain a particular route, you can add a `source` property referring to the route key and `target` property referring to the navigation state key:
596+
597+
```js
598+
import { StackActions } from '@react-navigation/native';
599+
600+
navigation.dispatch({
601+
...StackActions.retain(true),
602+
source: route.key,
603+
target: navigation.getState().key,
604+
});
605+
```

versioned_docs/version-8.x/stack-navigator.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,16 @@ Pops all of the screens in the stack except the first one and navigates to it.
585585
navigation.popToTop();
586586
```
587587

588+
#### `retain`
589+
590+
Marks or unmarks the current screen in the stack to be retained after it is removed from the history. The method accepts the following argument:
591+
592+
- `enable` - _boolean_ - Whether to retain the screen.
593+
594+
```js
595+
navigation.retain(true);
596+
```
597+
588598
### Hooks
589599

590600
The stack navigator exports the following hooks:

versioned_docs/version-8.x/upgrading-from-7.x.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,10 @@ Similarly, if you were using `state.preloadedRoutes` to get the preloaded routes
333333
+ const preloadedRoutes = state.routes.slice(state.index + 1);
334334
```
335335

336+
These changes also enable the new [retain](#stack-and-native-stack-navigators-now-support-retaining-screens) feature for Stack and Native Stack Navigators.
337+
338+
See [Navigation state](navigation-state.md) for more details.
339+
336340
##### Preloaded screens behave closer to regular screens
337341

338342
Previously, when a screen was preloaded in Stack and Native Stack Navigators, there were a few restrictions:
@@ -1048,6 +1052,29 @@ There is now a new `NavigationProvider` component that consolidates them, and ad
10481052

10491053
This is only necessary if you have custom components rendered in a navigator, e.g. buttons in a custom tab bar. Components rendered as part of a screen will have access to the `navigation` and `route` objects without any additional setup.
10501054

1055+
### Stack and Native Stack Navigators now support retaining screens
1056+
1057+
Stack and Native Stack Navigators now support retaining screens after they are removed from the navigation history.
1058+
1059+
Retaining a screen keeps it in the navigation state and rendered in the background, so local component state is preserved. The screen can later be brought back into focus with `navigate`:
1060+
1061+
```js
1062+
navigation.retain(true);
1063+
```
1064+
1065+
To stop retaining the current screen, call `retain(false)`:
1066+
1067+
```js
1068+
navigation.retain(false);
1069+
```
1070+
1071+
This can be useful in various scenarios:
1072+
1073+
- Keeping a frequently used heavy screen in memory to avoid unmounting and remounting it for better performance when navigating back and forth.
1074+
- Keeping a screen with a video or audio player rendered to enable functionality such as background playback or picture-in-picture mode when the user navigates away from the screen.
1075+
1076+
See [`retain`](stack-actions.md#retain) for more details.
1077+
10511078
### `Header` from `@react-navigation/elements` has been reworked
10521079

10531080
The `Header` component from `@react-navigation/elements` has been reworked with various improvements:

0 commit comments

Comments
 (0)