0
0
React Nativemobile~20 mins

Navigation state persistence in React Native - Mini App: Build & Ship

Choose your learning style9 modes available
Build: Home and Profile Navigation
This app has two screens: Home and Profile. Users can switch between them using bottom tabs. The app remembers which tab was last open, even after closing and reopening.
Target UI
┌─────────────────────────────┐
│          Home Screen         │
│                             │
│   Welcome to the Home!       │
│                             │
│  [Profile Tab] [Home Tab]    │
└─────────────────────────────┘
Use React Navigation with bottom tabs for Home and Profile screens.
Persist the navigation state so that when the app restarts, it opens the last visited tab.
Show simple text on each screen: 'Welcome to the Home!' and 'This is your Profile'.
Use AsyncStorage to save and load the navigation state.
Show a loading indicator while restoring the navigation state.
Starter Code
React Native
import React, { useState, useEffect } from 'react';
import { Text, View, ActivityIndicator } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

const Tab = createBottomTabNavigator();

function HomeScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Welcome to the Home!</Text>
    </View>
  );
}

function ProfileScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>This is your Profile</Text>
    </View>
  );
}

export default function App() {
  const [isReady, setIsReady] = useState(false);
  const [initialState, setInitialState] = useState();

  useEffect(() => {
    // TODO: Load navigation state from AsyncStorage here
  }, []);

  if (!isReady) {
    return <ActivityIndicator size="large" style={{ flex: 1, justifyContent: 'center' }} />;
  }

  return (
    <NavigationContainer
      // TODO: Pass initialState and onStateChange props here
    >
      <Tab.Navigator>
        <Tab.Screen name="Home" component={HomeScreen} />
        <Tab.Screen name="Profile" component={ProfileScreen} />
      </Tab.Navigator>
    </NavigationContainer>
  );
}
Task 1
Task 2
Task 3
Task 4
Solution
React Native
import React, { useState, useEffect } from 'react';
import { Text, View, ActivityIndicator } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

const PERSISTENCE_KEY = 'NAVIGATION_STATE_V1';
const Tab = createBottomTabNavigator();

function HomeScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Welcome to the Home!</Text>
    </View>
  );
}

function ProfileScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>This is your Profile</Text>
    </View>
  );
}

export default function App() {
  const [isReady, setIsReady] = useState(false);
  const [initialState, setInitialState] = useState();

  useEffect(() => {
    const restoreState = async () => {
      try {
        const savedStateString = await AsyncStorage.getItem(PERSISTENCE_KEY);
        const state = savedStateString ? JSON.parse(savedStateString) : undefined;
        if (state !== undefined) {
          setInitialState(state);
        }
      } catch (e) {
        // Ignore errors
      } finally {
        setIsReady(true);
      }
    };

    restoreState();
  }, []);

  if (!isReady) {
    return <ActivityIndicator size="large" style={{ flex: 1, justifyContent: 'center' }} />;
  }

  return (
    <NavigationContainer
      initialState={initialState}
      onStateChange={(state) => AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state))}
    >
      <Tab.Navigator>
        <Tab.Screen name="Home" component={HomeScreen} />
        <Tab.Screen name="Profile" component={ProfileScreen} />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

We use React Navigation's initialState prop to restore the last navigation state saved in AsyncStorage. When the app starts, useEffect runs once to load the saved state asynchronously. While loading, we show a spinner with ActivityIndicator.

Once loaded, we set initialState so the navigator opens the last visited tab. We also listen to navigation state changes with onStateChange and save the new state back to AsyncStorage. This way, the app remembers the last tab even after closing and reopening.

This approach improves user experience by keeping their place in the app.

Final Result
Completed Screen
┌─────────────────────────────┐
│          Home Screen         │
│                             │
│   Welcome to the Home!       │
│                             │
│  [Home Tab] [Profile Tab]    │
└─────────────────────────────┘
Tap 'Profile Tab' to switch to Profile screen showing 'This is your Profile'.
Tap 'Home Tab' to switch back to Home screen.
Close and reopen the app: it opens the last visited tab automatically.
Stretch Goal
Add a button on Profile screen to clear saved navigation state and reset to Home tab.
💡 Hint
Use AsyncStorage.removeItem with the persistence key and then reset navigation state using navigation.reset.