Lesson 03-React Native Navigation and Routing

Introduction to React Navigation

What is React Navigation?

React Navigation is a pure JavaScript navigation library designed for React Native. It provides a declarative API, supports common navigation patterns (e.g., stack, tabs, drawer), and is highly extensible. Compared to other navigation libraries, React Navigation’s advantages include:

  • Cross-Platform Support: Works seamlessly on both iOS and Android.
  • Flexibility: Allows customization of navigation behavior and styles.
  • Active Community: Offers extensive documentation and third-party plugin support.

Core components of React Navigation include:

  • Navigators: Such as StackNavigator, TabNavigator, and DrawerNavigator.
  • Navigation State: Manages the navigation state.
  • Navigation Actions: Triggers navigation behaviors (e.g., navigate, go back).

Installation and Environment Setup

Before starting, ensure you have initialized a React Native project using npx react-native init or Expo.

Install React Navigation
Run the following commands to install the core library and dependencies:

npm install @react-navigation/native
npm install react-native-screens react-native-safe-area-context
npm install @react-navigation/stack @react-navigation/bottom-tabs @react-navigation/drawer
npm install react-native-gesture-handler react-native-reanimated

Configure Native Dependencies
For iOS, run:

cd ios && pod install

For Android, ensure MainActivity.java is configured for react-native-gesture-handler:

import com.facebook.react.ReactActivity;
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;

public class MainActivity extends ReactActivity {
  @Override
  protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
      new MainReactPackage(),
      new RNGestureHandlerPackage()
    );
  }
}

Basic Project Structure
Set up a simple project structure:

MyApp/
├── src/
│   ├── screens/
│   │   ├── HomeScreen.js
│   │   ├── DetailsScreen.js
│   │   ├── ProfileScreen.js
│   ├── navigation/
│   │   ├── AppNavigator.js
├── App.js

Next, we’ll dive into specific navigator implementations.

StackNavigator (Stack Navigation)

Basic Concepts and Use Cases

StackNavigator is a stack-based navigation pattern, ideal for scenarios where pages are pushed and popped sequentially, such as:

  • Navigating from a home page to a details page.
  • Redirecting to a dashboard after user login.

Each new page is “pushed” onto the stack, and returning “pops” it off.

Implementing a Simple Stack Navigation

We’ll create a stack navigation with a home and details page.

Create Screen Components
In src/screens/HomeScreen.js:

import React from 'react';
import { View, Text, Button } from 'react-native';

const HomeScreen = ({ navigation }) => {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Home Screen</Text>
      <Button
        title="Go to Details"
        onPress={() => navigation.navigate('Details')}
      />
    </View>
  );
};

export default HomeScreen;

In src/screens/DetailsScreen.js:

import React from 'react';
import { View, Text, Button } from 'react-native';

const DetailsScreen = ({ navigation }) => {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Details Screen</Text>
      <Button title="Go Back" onPress={() => navigation.goBack()} />
    </View>
  );
};

export default DetailsScreen;

Configure the Navigator
In src/navigation/AppNavigator.js:

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from '../screens/HomeScreen';
import DetailsScreen from '../screens/DetailsScreen';

const Stack = createStackNavigator();

const AppNavigator = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName="Home">
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Details" component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

export default AppNavigator;

Use in App.js

import React from 'react';
import AppNavigator from './src/navigation/AppNavigator';

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

export default App;

Step-by-Step Analysis

  • NavigationContainer: The root container for managing navigation state.
  • createStackNavigator: Creates a stack navigator instance.
  • Stack.Screen: Defines each screen, with name as the route name and component as the corresponding component.
  • navigation.navigate: Navigates to a specified route.
  • navigation.goBack: Returns to the previous page.

After running the project, clicking “Go to Details” navigates to the details page, and “Go Back” returns to the home page.

Passing and Retrieving Route Parameters

In real-world apps, we often need to pass data between pages, such as a product ID from the home page to the details page.

Modify Code
In HomeScreen.js:

<Button
  title="Go to Details"
  onPress={() => navigation.navigate('Details', { itemId: 123, itemName: 'Sample Item' })}
/>

In DetailsScreen.js:

const DetailsScreen = ({ route, navigation }) => {
  const { itemId, itemName } = route.params; // Retrieve parameters
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Details Screen</Text>
      <Text>Item ID: {itemId}</Text>
      <Text>Item Name: {itemName}</Text>
      <Button title="Go Back" onPress={() => navigation.goBack()} />
    </View>
  );
};

Analysis

  • navigation.navigate(routeName, params): The second argument is an object for passing data.
  • route.params: Accesses passed parameters in the target screen via the route object.
  • For optional parameters, use defaults: const { itemId = 0, itemName = 'Unknown' } = route.params || {};

Customizing Navigation Options

StackNavigator supports customizing header styles, titles, etc.

Example Code
In AppNavigator.js:

<Stack.Navigator
  initialRouteName="Home"
  screenOptions={{
    headerStyle: { backgroundColor: '#f4511e' },
    headerTintColor: '#fff',
    headerTitleStyle: { fontWeight: 'bold' },
  }}
>
  <Stack.Screen
    name="Home"
    component={HomeScreen}
    options={{ title: 'Welcome Home' }}
  />
  <Stack.Screen
    name="Details"
    component={DetailsScreen}
    options={({ route }) => ({ title: route.params?.itemName || 'Details' })}
  />
</Stack.Navigator>

Analysis

  • screenOptions: Sets global navigation options.
  • options: Configures options for specific screens, either statically or dynamically via a function.
  • Dynamic titles use route.params to retrieve parameter values.

TabNavigator (Tab Navigation)

Basic Concepts and Use Cases

TabNavigator provides bottom tab navigation, ideal for quickly switching between parallel pages, such as:

  • Home, Search, Profile.

Creating a Tab Navigator

We’ll create a tab navigation with home and profile pages.

Code Implementation
In src/screens/ProfileScreen.js:

import React from 'react';
import { View, Text } from 'react-native';

const ProfileScreen = () => {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Profile Screen</Text>
    </View>
  );
};

export default ProfileScreen;

In AppNavigator.js:

import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import HomeScreen from '../screens/HomeScreen';
import ProfileScreen from '../screens/ProfileScreen';

const Tab = createBottomTabNavigator();

const AppNavigator = () => {
  return (
    <NavigationContainer>
      <Tab.Navigator>
        <Tab.Screen name="Home" component={HomeScreen} />
        <Tab.Screen name="Profile" component={ProfileScreen} />
      </Tab.Navigator>
    </NavigationContainer>
  );
};

Analysis

  • createBottomTabNavigator: Creates a bottom tab navigator.
  • After running, two tabs appear at the bottom, allowing page switching.

Customizing Tab Styles and Icons

We can add icons and customize tab styles.

Install Icon Library

npm install @expo/vector-icons

Modify Code

import { Ionicons } from '@expo/vector-icons';

const AppNavigator = () => {
  return (
    <NavigationContainer>
      <Tab.Navigator
        screenOptions={({ route }) => ({
          tabBarIcon: ({ focused, color, size }) => {
            let iconName;
            if (route.name === 'Home') {
              iconName = focused ? 'home' : 'home-outline';
            } else if (route.name === 'Profile') {
              iconName = focused ? 'person' : 'person-outline';
            }
            return <Ionicons name={iconName} size={size} color={color} />;
          },
          tabBarActiveTintColor: 'tomato',
          tabBarInactiveTintColor: 'gray',
        })}
      >
        <Tab.Screen name="Home" component={HomeScreen} />
        <Tab.Screen name="Profile" component={ProfileScreen} />
      </Tab.Navigator>
    </NavigationContainer>
  );
};

Analysis

  • tabBarIcon: Sets icons for each tab; focused indicates active state.
  • tabBarActiveTintColor and tabBarInactiveTintColor: Define colors for active and inactive tabs.

Passing Parameters in Tab Navigation

While TabNavigator is typically used for static pages, parameters can be passed. For example, navigating from home to profile with a user ID.

Modify HomeScreen

<Button
  title="Go to Profile"
  onPress={() => navigation.navigate('Profile', { userId: 'user123' })}
/>

Modify ProfileScreen

const ProfileScreen = ({ route }) => {
  const { userId } = route.params || {};
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Profile Screen</Text>
      {userId && <Text>User ID: {userId}</Text>}
    </View>
  );
};

Analysis

  • Parameter passing is similar to StackNavigator.
  • Note: Frequent parameter passing in tab navigation is uncommon; global state management (e.g., Redux) is often preferred.

DrawerNavigator (Drawer Navigation)

Basic Concepts and Use Cases

DrawerNavigator provides a side drawer menu, suitable for secondary navigation options, such as:

  • Settings, Help pages.

Implementing Drawer Navigation

Code Implementation
In AppNavigator.js:

import { createDrawerNavigator } from '@react-navigation/drawer';
import HomeScreen from '../screens/HomeScreen';
import ProfileScreen from '../screens/ProfileScreen';

const Drawer = createDrawerNavigator();

const AppNavigator = () => {
  return (
    <NavigationContainer>
      <Drawer.Navigator initialRouteName="Home">
        <Drawer.Screen name="Home" component={HomeScreen} />
        <Drawer.Screen name="Profile" component={ProfileScreen} />
      </Drawer.Navigator>
    </NavigationContainer>
  );
};

Analysis

  • createDrawerNavigator: Creates a drawer navigator.
  • By default, swiping from the left edge or clicking a menu button (manually implemented) opens the drawer.

Customizing Drawer Content and Styles

We can customize the drawer’s content, such as adding a user avatar.

Custom Drawer Component

import { DrawerContentScrollView, DrawerItemList } from '@react-navigation/drawer';

const CustomDrawerContent = (props) => {
  return (
    <DrawerContentScrollView {...props}>
      <View style={{ padding: 20, borderBottomWidth: 1, borderBottomColor: '#ccc' }}>
        <Text>User Name</Text>
      </View>
      <DrawerItemList {...props} />
    </DrawerContentScrollView>
  );
};

const AppNavigator = () => {
  return (
    <NavigationContainer>
      <Drawer.Navigator drawerContent={(props) => <CustomDrawerContent {...props} />}>
        <Drawer.Screen name="Home" component={HomeScreen} />
        <Drawer.Screen name="Profile" component={ProfileScreen} />
      </Drawer.Navigator>
    </NavigationContainer>
  );
};

Analysis

  • drawerContent: Passes a custom drawer component.
  • DrawerItemList: Renders the default navigation item list.

Parameter Passing in Drawer Navigation

Similar to previous navigators, parameters can be passed via navigate. For example:

navigation.navigate('Profile', { userId: 'user456' });

Comprehensive Case Study: Combining Multiple Navigators

Requirements Analysis

Suppose we want to build an app with:

  • Bottom tab navigation for “Home” and “Profile”.
  • A stack navigation within “Home” to navigate to a details page.
  • A side drawer for a settings page.

Step-by-Step Code Implementation

In AppNavigator.js:

import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createDrawerNavigator } from '@react-navigation/drawer';
import HomeScreen from '../screens/HomeScreen';
import DetailsScreen from '../screens/DetailsScreen';
import ProfileScreen from '../screens/ProfileScreen';
import { Ionicons } from '@expo/vector-icons';

// Home Stack
const HomeStack = createStackNavigator();
const HomeStackNavigator = () => {
  return (
    <HomeStack.Navigator>
      <HomeStack.Screen name="Home" component={HomeScreen} />
      <HomeStack.Screen name="Details" component={DetailsScreen} />
    </HomeStack.Navigator>
  );
};

// Tab Navigator
const Tab = createBottomTabNavigator();
const TabNavigator = () => {
  return (
    <Tab.Navigator
      screenOptions={({ route }) => ({
        tabBarIcon: ({ focused, color, size }) => {
          let iconName;
          if (route.name === 'HomeTab') iconName = focused ? 'home' : 'home-outline';
          else if (route.name === 'Profile') iconName = focused ? 'person' : 'person-outline';
          return <Ionicons name={iconName} size={size} color={color} />;
        },
      })}
    >
      <Tab.Screen name="HomeTab" component={HomeStackNavigator} options={{ headerShown: false }} />
      <Tab.Screen name="Profile" component={ProfileScreen} />
    </Tab.Navigator>
  );
};

// Drawer Navigator
const Drawer = createDrawerNavigator();
const AppNavigator = () => {
  return (
    <NavigationContainer>
      <Drawer.Navigator>
        <Drawer.Screen name="Main" component={TabNavigator} options={{ headerShown: false }} />
        <Drawer.Screen name="Settings" component={ProfileScreen} />
      </Drawer.Navigator>
    </NavigationContainer>
  );
};

export default AppNavigator;

Analysis

  • Nested Navigation: Drawer contains Tab, which contains Stack.
  • headerShown: false: Hides default headers in child navigators to avoid duplication.
  • The app now features a fully functional drawer, tab, and stack navigation.

Advanced Techniques and Considerations

Dynamic Route Configuration

Routes can be generated dynamically using state:

const [routes, setRoutes] = useState(['Home', 'Profile']);
<Stack.Navigator>
  {routes.map((route) => (
    <Stack.Screen key={route} name={route} component={ScreenMap[route]} />
  ))}
</Stack.Navigator>

Listen for page focus events:

React.useEffect(() => {
  const unsubscribe = navigation.addListener('focus', () => {
    console.log('Screen focused');
  });
  return unsubscribe;
}, [navigation]);

Performance Optimization Tips

  • Prevent Re-renders: Wrap screen components with React.memo.
  • Minimize Navigator Nesting: Excessive nesting can degrade performance.
  • Lazy Loading: Enable lazy: true in TabNavigator.

Share your love