Skip to content

Commit 687409c

Browse files
committed
Document how history stack works
1 parent d942ec1 commit 687409c

File tree

2 files changed

+111
-31
lines changed

2 files changed

+111
-31
lines changed

versioned_docs/version-7.x/navigation-state.md

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ There are few properties present in every navigation state object:
2929
- `routeNames` - Name of the screens defined in the navigator. This is an unique array containing strings for each screen.
3030
- `routes` - List of route objects (screens) which are rendered in the navigator. It also represents the history in a stack navigator. There should be at least one item present in this array.
3131
- `index` - Index of the focused route object in the `routes` array.
32-
- `history` - A list of visited items. This is an optional property and not present in all navigators. For example, it's only present in tab and drawer navigators in the core. The shape of the items in the `history` array can vary depending on the navigator. There should be at least one item present in this array.
32+
- `history` - An optional list of visited items. See [History stack](#history-stack) for more details.
3333
- `stale` - A navigation state is assumed to be stale unless the `stale` property is explicitly set to `false`. This means that the state object needs to be ["rehydrated"](#stale-state-objects).
3434

3535
Each route object in a `routes` array may contain the following properties:
@@ -67,30 +67,59 @@ const state = {
6767
};
6868
```
6969

70-
It's important to note that even if there's a nested navigator, the `state` property on the `route` object is not added until a navigation happens, hence it's not guaranteed to exist.
70+
It's important to note that even if there's a nested navigator, the `state` property on the `route` object is not added until a navigation happens, hence it's not guaranteed to exist, or maybe [stale](#stale-state-objects).
7171

72-
## Stale state objects
73-
74-
Earlier there was a mention of `stale` property in the navigation state. If the `stale` property is set to `true` or is missing, the state is assumed to be stale. A stale navigation state means that the state object may be partial, such as missing keys or routes, contain invalid routes, or may not be up-to-date. A stale state can be a result of [deep linking](deep-linking.md), r[estoring from a persisted state](state-persistence.md) etc.
72+
## History stack
7573

76-
If you're accessing the navigation state of a navigator using the built-in APIs such as [`useNavigationState()`](use-navigation-state.md), [`navigation.getState()`](navigation-object.md#getstate) etc., the state object is guaranteed to be complete and not stale. However, if you try to access a child navigator's state with the `state` property on the `route` object, it maybe a stale or partial state object. So it's not recommended to use this property directly.
74+
In React Navigation, each navigator may maintain a history stack to keep track of visited entries. This is used when navigating back, syncing with browser history on the Web, etc.
7775

78-
Using the [`ref.getRootState()`](navigation-container.md#getrootstate) API will always return a complete and up-to-date state object for the current navigation tree, including any nested child navigators.
76+
Unlike Web, which has a linear history stack, React Navigation uses a nested history stack mirroring mobile navigation patterns. A parent navigator maintains its own history stack, while each child navigator also maintains its own history stack. When navigating back, it goes back in the history stack of the navigator where the "go back" action was triggered - and if that stack is empty, it bubbles up to the parent navigator's history stack. Any sibling navigators' history stacks are not affected.
7977

80-
When React Navigation encounters stale or partial state, it will automatically fix it up before using it. This includes adding missing keys, removing any invalid routes, ensuring the `index` is correct etc. This process of fixing stale state is called **rehydration**. If you're writing a [custom router](custom-routers.md), the `getRehydratedState` method lets you write custom rehydration logic to fix up state objects.
78+
The history stack for a navigator is determined from `state.history` if present, otherwise `state.routes` is used.
8179

82-
For example, `index` should be the last route in a stack, and if a different value was specified, React Navigation fixes it. For example, if you wanted to reset your app's navigation state to have it display the `Profile` route, and have the `Home` route displayed upon going back, and dispatched the following action:
80+
The content and shape of items in the `state.history` array can vary depending on the navigator. There should be at least one item present in this array. Among built-in navigators, this property is present only in tab and drawer navigators. For example, the `history` array in a drawer navigator looks like this:
8381

8482
```js
85-
navigation.reset({
86-
index: 0,
87-
routes: [{ name: 'Home' }, { name: 'Profile' }],
88-
});
83+
const state = {
84+
history: [
85+
{ type: 'route', key: 'home-1' },
86+
{ type: 'route', key: 'settings-1' },
87+
{ type: 'drawer', status: 'open' },
88+
],
89+
90+
// ...
91+
};
8992
```
9093

91-
React Navigation would correct `index` to `1` before the routes are displayed.
94+
This array is populated based on the `backBehavior` prop of the tab or drawer navigators:
95+
96+
- `firstRoute` - the first route defined in the navigator and the focused route
97+
- `initialRoute` - the initial route defined in the navigator and the focused route
98+
- `order` - the focused route and any routes defined before it in the navigator, in the order they are defined
99+
- `history` - deduplicated list of previously visited routes in the navigator and the focused route
100+
- `fullHistory` - full list of previously visited routes in the navigator and the focused route
101+
- `none` - only the focused route
102+
103+
[Custom routers](custom-routers.md) may also add different types of items to the `history` array to represent different kinds of history entries.
104+
105+
## Stale state objects
106+
107+
Earlier there was a mention of `stale` property in the navigation state. If the `stale` property is set to `true` or is missing, the state is assumed to be stale. Typically this is not something to worry about unless you're using the navigation state object directly for advanced use-cases.
108+
109+
A stale navigation state means that the state object may be partial, such as missing keys or routes, contain invalid routes, or may not be up-to-date. A stale state can be a result of [deep linking](deep-linking.md), [restoring from a persisted state](state-persistence.md) etc.
110+
111+
The state object is guaranteed to not be stale when accessing it with built-in APIs such as:
112+
113+
- Navigator's state with [`useNavigationState()`](use-navigation-state.md) or [`navigation.getState()`](navigation-object.md#getstate) - not including child navigators.
114+
- Complete state of the navigation tree with [`ref.getRootState()`](navigation-container.md#getrootstate) including root navigator and all child navigators.
115+
116+
However, if you try to access a child navigator's state with the `state` property on the [`route`](route.md) object, it maybe a stale or partial state object. So it's not recommended to use this property directly.
117+
118+
When React Navigation encounters stale or partial state, it will automatically fix it up before using it. This includes adding missing keys, removing any invalid routes, ensuring the `index` is correct etc. This process of fixing stale state is called **rehydration**. If you're writing a [custom router](custom-routers.md), the `getRehydratedState` method lets you write custom rehydration logic to fix up state objects.
119+
120+
This feature comes handy when doing operations such as [reset](navigation-actions.md#reset), [providing a initial state](navigation-container.md#initialstate) etc., as you can safely omit many properties from the navigation state object and relying on React Navigation to add those properties for you, making your code simpler.
92121

93-
This feature comes handy when doing operations such as [reset](navigation-actions.md#reset), [providing a initial state](navigation-container.md#initialstate) etc., as you can safely omit many properties from the navigation state object and relying on React Navigation to add those properties for you, making your code simpler. For example, you can only provide a `routes` array without any keys and React Navigation will automatically add everything that's needed to make it work:
122+
For example, you can only provide a state without `index`, `keys` etc. only with a `routes` array without any keys and React Navigation will automatically add everything that's needed to make it work:
94123

95124
```js
96125
const state = {
@@ -120,7 +149,7 @@ It's also possible to provide invalid data such as non-existent screens and it'l
120149

121150
:::tip
122151

123-
If you want React Navigation to fix invalid state, you need to make sure that you don't have `stale: false` in the state object. State objects with `stale: false` are assumed to be valid state objects and React Navigation won't attempt to fix them.
152+
If you want React Navigation to fix invalid state, make sure that you don't have `stale: false` in the state object. State objects with `stale: false` are assumed to be valid state objects and React Navigation won't attempt to fix them. If `stale` is missing or set to `true`, React Navigation will always try to rehydrate the state object.
124153

125154
:::
126155

versioned_docs/version-8.x/navigation-state.md

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ There are few properties present in every navigation state object:
2929
- `routeNames` - Name of the screens defined in the navigator. This is an unique array containing strings for each screen.
3030
- `routes` - List of route objects (screens) which are rendered in the navigator. It also represents the history in a stack navigator. There should be at least one item present in this array.
3131
- `index` - Index of the focused route object in the `routes` array.
32-
- `history` - A list of visited items. This is an optional property and not present in all navigators. For example, it's only present in tab and drawer navigators in the core. The shape of the items in the `history` array can vary depending on the navigator. There should be at least one item present in this array.
32+
- `history` - An optional list of visited items. See [History stack](#history-stack) for more details.
3333
- `stale` - A navigation state is assumed to be stale unless the `stale` property is explicitly set to `false`. This means that the state object needs to be ["rehydrated"](#stale-state-objects).
3434

3535
Each route object in a `routes` array may contain the following properties:
3636

3737
- `key` - Unique key of the screen. Created automatically or added while navigating to this screen.
3838
- `name` - Name of the screen. Defined in navigator component hierarchy.
3939
- `params` - An optional object containing params which is defined while navigating e.g. `navigate('Home', { sortBy: 'latest' })`.
40+
- `history` - An optional list of history items for the route. See [History stack](#history-stack) for more details.
4041
- `state` - An optional object containing the [stale navigation state](#stale-state-objects) of a child navigator nested inside this screen.
4142

4243
For example, a stack navigator containing a tab navigator nested inside it's home screen may have a navigation state object like this:
@@ -67,30 +68,80 @@ const state = {
6768
};
6869
```
6970

70-
It's important to note that even if there's a nested navigator, the `state` property on the `route` object is not added until a navigation happens, hence it's not guaranteed to exist.
71+
It's important to note that even if there's a nested navigator, the `state` property on the `route` object is not added until a navigation happens, hence it's not guaranteed to exist, or maybe [stale](#stale-state-objects).
7172

72-
## Stale state objects
73+
## History stack
7374

74-
Earlier there was a mention of `stale` property in the navigation state. If the `stale` property is set to `true` or is missing, the state is assumed to be stale. A stale navigation state means that the state object may be partial, such as missing keys or routes, contain invalid routes, or may not be up-to-date. A stale state can be a result of [deep linking](deep-linking.md), r[estoring from a persisted state](state-persistence.md) etc.
75+
In React Navigation, each navigator may maintain a history stack to keep track of visited entries. This is used when navigating back, syncing with browser history on the Web, etc.
7576

76-
If you're accessing the navigation state of a navigator using the built-in APIs such as [`useNavigationState()`](use-navigation-state.md), [`navigation.getState()`](navigation-object.md#getstate) etc., the state object is guaranteed to be complete and not stale. However, if you try to access a child navigator's state with the `state` property on the `route` object, it maybe a stale or partial state object. So it's not recommended to use this property directly.
77+
Unlike Web, which has a linear history stack, React Navigation uses a nested history stack mirroring mobile navigation patterns. A parent navigator maintains its own history stack, while each child navigator also maintains its own history stack. When navigating back, it goes back in the history stack of the navigator where the "go back" action was triggered - and if that stack is empty, it bubbles up to the parent navigator's history stack. Any sibling navigators' history stacks are not affected.
7778

78-
Using the [`ref.getRootState()`](navigation-container.md#getrootstate) API will always return a complete and up-to-date state object for the current navigation tree, including any nested child navigators.
79+
The history stack for a navigator is determined from the following sources:
7980

80-
When React Navigation encounters stale or partial state, it will automatically fix it up before using it. This includes adding missing keys, removing any invalid routes, ensuring the `index` is correct etc. This process of fixing stale state is called **rehydration**. If you're writing a [custom router](custom-routers.md), the `getRehydratedState` method lets you write custom rehydration logic to fix up state objects.
81+
- `state.history` if present, otherwise `state.routes`
82+
- `route.history` for each route in `state.routes`
8183

82-
For example, `index` should be the last route in a stack, and if a different value was specified, React Navigation fixes it. For example, if you wanted to reset your app's navigation state to have it display the `Profile` route, and have the `Home` route displayed upon going back, and dispatched the following action:
84+
When determining total length of history stack (e.g. to sync with browser history on the Web), these 2 sources are combined.
85+
86+
The content and shape of items in the `state.history` array can vary depending on the navigator. There should be at least one item present in this array. Among built-in navigators, this property is present only in tab and drawer navigators. For example, the `history` array in a drawer navigator looks like this:
8387

8488
```js
85-
navigation.reset({
86-
index: 0,
87-
routes: [{ name: 'Home' }, { name: 'Profile' }],
88-
});
89+
const state = {
90+
history: [
91+
{ type: 'route', key: 'home-1' },
92+
{ type: 'route', key: 'settings-1' },
93+
{ type: 'drawer', status: 'open' },
94+
],
95+
96+
// ...
97+
};
98+
```
99+
100+
This array is populated based on the `backBehavior` prop of the tab or drawer navigators:
101+
102+
- `firstRoute` - the first route defined in the navigator and the focused route
103+
- `initialRoute` - the initial route defined in the navigator and the focused route
104+
- `order` - the focused route and any routes defined before it in the navigator, in the order they are defined
105+
- `history` - deduplicated list of previously visited routes in the navigator and the focused route
106+
- `fullHistory` - full list of previously visited routes in the navigator and the focused route
107+
- `none` - only the focused route
108+
109+
Similarly, each `route` object in the `routes` array may also have a `history` property which maintains a history stack for that specific route. It contains previous params in built-in navigators:
110+
111+
```js
112+
const route = {
113+
history: [
114+
{ type: 'params', params: { sortBy: 'latest' } },
115+
{ type: 'params', params: { sortBy: 'popular' } },
116+
],
117+
params: { sortBy: 'trending' },
118+
119+
// ...
120+
};
89121
```
90122

91-
React Navigation would correct `index` to `1` before the routes are displayed.
123+
By default, this property is populated when using the [`pushParams`](navigation-actions.md#pushparams) action. The `params` property on the route object always contains the latest params, while the `history` array contains previous params in the order they were updated.
124+
125+
[Custom routers](custom-routers.md) may also add different types of items to the `history` array to represent different kinds of history entries.
126+
127+
## Stale state objects
128+
129+
Earlier there was a mention of `stale` property in the navigation state. If the `stale` property is set to `true` or is missing, the state is assumed to be stale. Typically this is not something to worry about unless you're using the navigation state object directly for advanced use-cases.
130+
131+
A stale navigation state means that the state object may be partial, such as missing keys or routes, contain invalid routes, or may not be up-to-date. A stale state can be a result of [deep linking](deep-linking.md), [restoring from a persisted state](state-persistence.md) etc.
132+
133+
The state object is guaranteed to not be stale when accessing it with built-in APIs such as:
134+
135+
- Navigator's state with [`useNavigationState()`](use-navigation-state.md) or [`navigation.getState()`](navigation-object.md#getstate) - not including child navigators.
136+
- Complete state of the navigation tree with [`ref.getRootState()`](navigation-container.md#getrootstate) including root navigator and all child navigators.
137+
138+
However, if you try to access a child navigator's state with the `state` property on the [`route`](route.md) object, it maybe a stale or partial state object. So it's not recommended to use this property directly.
139+
140+
When React Navigation encounters stale or partial state, it will automatically fix it up before using it. This includes adding missing keys, removing any invalid routes, ensuring the `index` is correct etc. This process of fixing stale state is called **rehydration**. If you're writing a [custom router](custom-routers.md), the `getRehydratedState` method lets you write custom rehydration logic to fix up state objects.
141+
142+
This feature comes handy when doing operations such as [reset](navigation-actions.md#reset), [providing a initial state](navigation-container.md#initialstate) etc., as you can safely omit many properties from the navigation state object and relying on React Navigation to add those properties for you, making your code simpler.
92143

93-
This feature comes handy when doing operations such as [reset](navigation-actions.md#reset), [providing a initial state](navigation-container.md#initialstate) etc., as you can safely omit many properties from the navigation state object and relying on React Navigation to add those properties for you, making your code simpler. For example, you can only provide a `routes` array without any keys and React Navigation will automatically add everything that's needed to make it work:
144+
For example, you can only provide a state without `index`, `keys` etc. only with a `routes` array without any keys and React Navigation will automatically add everything that's needed to make it work:
94145

95146
```js
96147
const state = {
@@ -120,7 +171,7 @@ It's also possible to provide invalid data such as non-existent screens and it'l
120171

121172
:::tip
122173

123-
If you want React Navigation to fix invalid state, you need to make sure that you don't have `stale: false` in the state object. State objects with `stale: false` are assumed to be valid state objects and React Navigation won't attempt to fix them.
174+
If you want React Navigation to fix invalid state, make sure that you don't have `stale: false` in the state object. State objects with `stale: false` are assumed to be valid state objects and React Navigation won't attempt to fix them. If `stale` is missing or set to `true`, React Navigation will always try to rehydrate the state object.
124175

125176
:::
126177

0 commit comments

Comments
 (0)