DrRefactor 16f0e11822 fix: prevent navigation state updates after state cleanup (#9688)
Problem:
When using nested navigators, unmounts cause race cleanup races.

Imagine following hierarchy (from tree root downwards, parent to children):
TabNavigator (1) [renders useNavigationBuilder]
  SceneView (from TabNavigator)
StackNavigators (N) [each renders useNavigationBuilder] 
  SceneView (from StackNavigator)

Now lets test following flow:
1. Mount above navigators with given navigation params (e.g. navigation for unauthenticated users) 
2. Unmount all navigators (e.g. during login process)
3. Mount above navigation with different navigation params than in 1) (e.g. navigation for authenticated users)

What you'll observe, there will be old navigation params preserved in 3) coming from 1).

Source of problem:
BaseNavigationContainer holds global navigation state, exposes API to modify it via NavigationStateContext. When useNavigationBuilder unmounts, it attempts to clear navigation state. (see cleanup effect in useNavigationBuilder.tsx).

(I) First clear occurs in TabNavigator's effect, which successfully clears BaseNavigationContainer's state (sets it to undefined).

(II) Second clear comes from StackNavigator unmount. It's useNavigationBuilder cleanup effect also calls NavigationStateContext.setState(undefined).
But this time - we meet SceneView as closest NavigationStateContext.Provider. SceneView attempts to merge state change with current navigation state, which is reasonable. But current navigation state should be already undefined... It is, but:
```
[useNavigationBuilder.tsx]

const getState = React.useCallback((): State => {
    const currentState = getCurrentState();

    return isStateInitialized(currentState)
      ? (currentState as State)
      : (initializedStateRef.current as State);
  }, [getCurrentState, isStateInitialized]);
```
"undefined" state is treated is non-initialized state, and freshly computed state (initializedStateRef.current) is returned instead.
SceneView does merge this old state with `undefined` value, and passes to BaseNavigationContainer. Now we have some legacy global state, despite all navigators being unmounted.

After mounting navigators again (3), we can observe old params being restored. These params might come e.g. from old `initialParams` prop (from 1)).

Solution:
Do not propagate `setState` upwards in `useNavigationBuilder` after state cleanup. This way we'll omit such races.
2021-07-16 10:29:26 +02:00
2021-06-30 09:50:49 +02:00
2020-03-22 23:58:06 +01:00
2021-06-05 07:33:26 +02:00
2020-02-19 23:30:12 +01:00
2021-04-08 06:14:41 +02:00
2020-04-12 22:11:22 +02:00
2021-06-05 07:33:26 +02:00
2020-06-22 16:23:20 +02:00
2021-06-05 07:33:26 +02:00

React Navigation 5

Build Status Code Coverage MIT License

Routing and navigation for your React Native apps.

Documentation can be found at reactnavigation.org.

If you are looking for version 4, the code can be found in the 4.x branch.

Package Versions

Name Latest Version
@react-navigation/core badge
@react-navigation/native badge
@react-navigation/routers badge
@react-navigation/stack badge
@react-navigation/drawer badge
@react-navigation/material-top-tabs badge
@react-navigation/material-bottom-tabs badge
@react-navigation/bottom-tabs badge
@react-navigation/devtools badge

Contributing

Please read through our contribution guide to get started!

Installing from a fork on GitHub

Since we use a monorepo, it's not possible to install a package from the repository URL. If you need to install a forked version from Git, you can use gitpkg.

First install gitpkg:

yarn global add gitpkg

Then follow these steps to publish and install a forked package:

  1. Fork this repo to your account and clone the forked repo to your local machine
  2. Open a Terminal and cd to the location of the cloned repo
  3. Run yarn to install any dependencies
  4. If you want to make any changes, make them and commit
  5. Now cd to the package directory that you want to use (e.g. cd packages/stack for @react-navigation/stack)
  6. Run gitpkg publish to publish the package to your repo

After publishing, you should see something like this:

Package uploaded to git@github.com:<user>/<repo>.git with the name <name>

You can now install the dependency in your project:

yarn add <user>/<repo>.git#<name>

Remember to replace <user>, <repo> and <name> with right values.

Description
No description provided
Readme 46 MiB
Languages
TypeScript 97.9%
Java 0.8%
JavaScript 0.6%
Objective-C 0.4%
Starlark 0.2%