Persist state with react-navigation AppContainer

Viewed 2046

I updated react-navigation for a react-native project from v2.x to v3.x. For the v2.x I had this rendered at root:

const AppNavigator = createStackNavigator({...})

const App = () => <AppNavigator persistenceKey={"NavigationState"} />;

export default App; 

I need to persist the state, thats why I used persistenceKey

For the v3.x of react-navigation an app container is required, but I'm having problems figuring out how to implement the same state persistence.

This is my new code with the v3.x

const AppNavigator = createStackNavigator({...})

const AppContainer = createAppContainer(AppNavigator)

const App = () => <AppContainer />;

export default App;

How do I persist the state this way?

Thanks

EDIT:

I've tried this:

const AppNavigator = createStackNavigator({...})

const persistenceKey = "persistenceKey"
  const persistNavigationState = async (navState) => {
    try {
      await AsyncStorage.setItem(persistenceKey, JSON.stringify(navState))
    } catch(err) {
      // handle the error according to your needs
    }
  }
  const loadNavigationState = async () => {
    const jsonString = await AsyncStorage.getItem(persistenceKey)
    return JSON.parse(jsonString)
  }

const AppNavigationPersists = () => <AppNavigator
  persistNavigationState={persistNavigationState}
  loadNavigationState={loadNavigationState}
/>

const AppContainer = createAppContainer(AppNavigationPersists)

export default AppContainer;

but I get this error:

Cannot read property 'getStateForAction' of undefined

5 Answers

You may need to update react-navigation to >= 3.10.0.

Per the react-navigation changelog, they only now only support persistNavigationState and loadNavigationState on react-navigation@^3.10.

You can still use persistenceKey on versions lower than 3.10.

---EDIT---

An example of a version <3.10.0:

const AppNavigator = createStackNavigator({...})

const App = () => <AppNavigator persistenceKey={"NavigationState"} />;

export default App; 

An example implementation for a version >= 3.10.0:

const AppNavigator = createStackNavigator({...});
const persistenceKey = "persistenceKey"
const persistNavigationState = async (navState) => {
  try {
    await AsyncStorage.setItem(persistenceKey, JSON.stringify(navState))
  } catch(err) {
    // handle the error according to your needs
  }
}
const loadNavigationState = async () => {
  const jsonString = await AsyncStorage.getItem(persistenceKey)
  return JSON.parse(jsonString)
}

const App = () => <AppNavigator persistNavigationState={persistNavigationState} loadNavigationState={loadNavigationState} />;

You can check the docs example for v3.x.

const AppNavigator = createStackNavigator({...});
  const persistenceKey = "persistenceKey"
  const persistNavigationState = async (navState) => {
    try {
      await AsyncStorage.setItem(persistenceKey, JSON.stringify(navState))
    } catch(err) {
      // handle the error according to your needs
    }
  }
  const loadNavigationState = async () => {
    const jsonString = await AsyncStorage.getItem(persistenceKey)
    return JSON.parse(jsonString)
  }

 const App = () => <AppNavigator persistNavigationState={persistNavigationState} loadNavigationState={loadNavigationState} />;

To solve the problem in const AppContainer = createAppContainer(AppNavigator) what you can do is create another component that return AppNavigator with the persistence.

const AppNavigationPersists = () => <AppNavigator 
    persistNavigationState={persistNavigationState} 
    loadNavigationState={loadNavigationState} 
/>
const AppContainer = createAppContainer(AppNavigationPersists)

Just implement it the same way as in the example. In my case I forgot to update react-navigation to version ^3.11.0 and I also forgot to import AsyncStorage. Somehow react native wasn't complaining about AsyncStorage not being there. This is why the state persistence didn't seem to work.

import {
  AsyncStorage
} from "react-native";

const AppNavigator = createStackNavigator({...});

const AppContainer = createAppContainer(AppNavigator);

const persistenceKey = "persistenceKey"

const persistNavigationState = async (navState) => {
  try {
    await AsyncStorage.setItem(persistenceKey, JSON.stringify(navState))
  } catch(err) {
    // handle the error according to your needs
  }
}

const loadNavigationState = async () => {
  const jsonString = await AsyncStorage.getItem(persistenceKey)
  return JSON.parse(jsonString)
}

const App = () => <AppContainer persistNavigationState={persistNavigationState} loadNavigationState={loadNavigationState}  renderLoadingExperimental={() => <ActivityIndicator />}/>;

The doc is not clear but you actually need to pass the props persistNavigationState and loadNavigationState directly to the AppContainer component:

<AppContainer
  persistNavigationState={persistNavigationState}
  loadNavigationState={loadNavigationState}
/>

If you are using createAppContainer with react navigation 4, here is the solution which worked for me.

const App: () => React$Node = () => {

    const persistenceKey = "persistenceKey"
    const persistNavigationState = async (navState) => {
        try {
            await AsyncStorage.setItem(persistenceKey, JSON.stringify(navState))
        } catch(err) {
            // handle error
        }
    }
    const loadNavigationState = async () => {
        const jsonString = await AsyncStorage.getItem(persistenceKey)
        return JSON.parse(jsonString)
    }
    return(
        <View style={{flex: 1, backgroundColor: '#000000'}}>
            <AppContainer
                persistNavigationState={persistNavigationState}
                loadNavigationState={loadNavigationState}
            />
        </View>
    );
};
Related