Bird
Raised Fist0
React Nativemobile~20 mins

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

Choose your learning style10 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.

Practice

(1/5)
1. What is the main purpose of navigation state persistence in a React Native app?
easy
A. To change the app's theme based on user preference
B. To speed up the app's loading time by caching images
C. To save the user's current screen so they return to it after closing the app
D. To reset the app to the home screen every time it opens

Solution

  1. Step 1: Understand navigation state persistence

    Navigation state persistence means saving where the user is in the app so they can continue from the same place later.
  2. Step 2: Identify the correct purpose

    Only To save the user's current screen so they return to it after closing the app describes saving the current screen to restore it after app restart.
  3. Final Answer:

    To save the user's current screen so they return to it after closing the app -> Option C
  4. Quick Check:

    Navigation state persistence = save current screen [OK]
Hint: Remember: persistence means saving state for later use [OK]
Common Mistakes:
  • Confusing navigation state with app theme or cache
  • Thinking it resets app screens instead of saving them
2. Which of the following is the correct way to use NavigationContainer for state persistence?
easy
A.
B.
C.
D.

Solution

  1. Step 1: Recall NavigationContainer props for persistence

    The correct props are initialState to set saved state and onStateChange to save new state.
  2. Step 2: Match options with correct props

    Only uses initialState and onStateChange correctly.
  3. Final Answer:

    <NavigationContainer initialState={savedState} onStateChange={saveState}> -> Option D
  4. Quick Check:

    Use initialState and onStateChange props [OK]
Hint: Look for exact prop names: initialState and onStateChange [OK]
Common Mistakes:
  • Using wrong prop names like state or onChange
  • Swapping savedState and saveState variables
3. Given this code snippet, what will happen after the user navigates to 'Profile' and the app restarts?
const [initialState, setInitialState] = React.useState();
React.useEffect(() => {
  AsyncStorage.getItem('NAV_STATE').then(state => {
    if(state) setInitialState(JSON.parse(state));
  });
}, []);

 AsyncStorage.setItem('NAV_STATE', JSON.stringify(state))}
>
  {/* screens */}
;
medium
A. The app opens on the default initial screen ignoring saved state
B. The app opens on the 'Profile' screen automatically
C. The app crashes due to missing navigation state
D. The app opens on a blank screen

Solution

  1. Step 1: Understand AsyncStorage usage

    The code loads saved navigation state from AsyncStorage and sets it as initialState.
  2. Step 2: Effect of initialState on NavigationContainer

    Passing initialState restores the last screen, so the app opens on 'Profile' after restart.
  3. Final Answer:

    The app opens on the 'Profile' screen automatically -> Option B
  4. Quick Check:

    Saved state restores last screen [OK]
Hint: Saved state in initialState restores last screen [OK]
Common Mistakes:
  • Assuming app ignores saved state and resets
  • Thinking missing state causes crash
4. Identify the bug in this code that tries to persist navigation state:
const [navState, setNavState] = React.useState();

React.useEffect(() => {
  AsyncStorage.getItem('NAV_STATE').then(state => {
    setNavState(state);
  });
}, []);

 AsyncStorage.setItem('NAV_STATE', JSON.stringify(state))}
>
  {/* screens */}
;
medium
A. The saved state is not parsed from JSON before setting navState
B. The onStateChange callback should not stringify the state
C. initialState should be set to null instead of undefined
D. AsyncStorage.getItem should be called inside onStateChange

Solution

  1. Step 1: Check AsyncStorage.getItem usage

    AsyncStorage.getItem returns a JSON string, so it must be parsed before use.
  2. Step 2: Identify missing JSON.parse

    The code sets navState directly to the string, causing navigation to fail.
  3. Final Answer:

    The saved state is not parsed from JSON before setting navState -> Option A
  4. Quick Check:

    Parse JSON before setting state [OK]
Hint: Always parse JSON from AsyncStorage before using [OK]
Common Mistakes:
  • Forgetting JSON.parse on saved state
  • Misusing onStateChange callback
5. You want to persist navigation state but only save it after the user has navigated at least once. Which approach correctly implements this behavior?
hard
A. Use a ref to track if navigation happened, then save state in onStateChange only if true
B. Save state immediately on app start inside useEffect
C. Call AsyncStorage.clear() before saving state to avoid duplicates
D. Set initialState to null and never update it

Solution

  1. Step 1: Understand conditional saving need

    We want to save navigation state only after user navigates once, so we track navigation occurrence.
  2. Step 2: Use a ref to track navigation

    A ref can store a boolean flag that updates on first navigation, then onStateChange saves state only if true.
  3. Step 3: Evaluate other options

    Save state immediately on app start inside useEffect saves immediately, ignoring condition; C clears storage unnecessarily; D disables persistence.
  4. Final Answer:

    Use a ref to track if navigation happened, then save state in onStateChange only if true -> Option A
  5. Quick Check:

    Track navigation with ref before saving state [OK]
Hint: Use a ref flag to save state only after first navigation [OK]
Common Mistakes:
  • Saving state too early before navigation
  • Clearing storage unnecessarily
  • Ignoring initialState updates
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard