Native Navigation for React Native

repository·master·Indexed 25 days ago

https://github.com/airbnb/native-navigation

A navigation library for React Native built on top of actual iOS and Android platform navigational components. It provides a core Navigator instance with methods like registerScreen, push, present, pop, and dismiss, along with components for configuration (Navigator.Config), tab bars (TabBar, Tab), and shared element transitions (SharedElement, SharedElementGroup).

Tokens
13K
Snippets
27
Records
73
Agent score
84%

What's inside native-navigation

  1. Explore Native Navigation guides

    master

    The native-navigation project provides several guides to help you implement and customize navigation in your mobile applications. Available guides include:

    • Basic Usage: Getting started with the core navigation features.
    • Integrating with existing apps: How to add native navigation to an app that already has an existing navigation structure.
    • Custom Navigation Implementations: Instructions for building your own navigation logic using the library's primitives.
    • Deep Linking: Configuring the library to handle incoming deep links for navigation.
    • Platform Differences: Understanding how navigation behavior varies between iOS and Android.
    • Project Structure: Guidance on how to organize your code when using this library.
    • Shared Element Transitions: Implementing smooth visual transitions between screens.
  2. Production Readiness Warning for Native Navigation

    master
    As of the current version, Native Navigation is in "beta" and is not recommended for production use. The library is undergoing refactoring to decouple it from Airbnb's internal infrastructure to improve extensibility. Users should be aware that much of the codebase has not yet been tested in a production environment.
  3. Organize your project structure for Native Navigation

    master

    Native Navigation uses a 'Screen' based architecture where each screen is a top-level React component registered individually. This allows for better code splitting and performance by preventing all code from executing at app start.

    A recommended directory structure separates components from screens and uses a central routes.js file for screen identifiers (similar to URLs) and an index.js file as the entry point for the JavaScript bundle.

    Recommended structure:

    root/
    ├── components/
    ├── screens/
    │   ├── FooScreen.js
    │   └── BarScreen.js
    ├── routes.js
    └── index.js
    // routes.js
    export const FOO = 'MyApp/Foo';
    export const BAR = 'MyApp/Bar';
    
    // index.js
    import Navigator from 'native-navigation';
    import { FOO, BAR } from './routes';
    
    Navigator.registerScreen(FOO, () => require('./screens/FooScreen'));
    Navigator.registerScreen(BAR, () => require('./screens/BarScreen'));
  4. Implement custom iOS navigation

    master

    To use a custom navigation solution on iOS, implement the ReactNavigationImplementation protocol. This protocol allows you to control how navigation controllers are created and how screen, tab, and tab bar configurations are reconciled with the native UI.

    Once implemented, inject your custom implementation into the ReactNavigationCoordinator singleton.

    // 1. Implement the protocol
    protocol ReactNavigationImplementation {
      func makeNavigationController(rootViewController: UIViewController) -> UINavigationController
    
      func reconcileScreenConfig(
        viewController: ReactViewController,
        navigationController: UINavigationController?,
        prev: [String: AnyObject],
        next: [String: AnyObject]
      )
    
      func reconcileTabConfig(
        tabBarItem: UITabBarItem,
        prev: [String: AnyObject],
        next: [String: AnyObject]
      )
    
      func reconcileTabBarConfig(
        tabBar: UITabBar,
        prev: [String: AnyObject],
        next: [String: AnyObject]
      )
    
      func getBarHeight(
        viewController: ReactViewController, 
        navigationController: UINavigationController?,
        config: [String: AnyObject]
      ) -> CGFloat
    }
    
    // 2. Inject the implementation
    let implementation: ReactNavigationImplementation = MyCustomNavigationImplementation();
    ReactNavigationCoordinator.sharedInstance.navigation = implementation;
  5. Implement custom Android navigation

    master

    To use a custom navigation solution on Android, implement the NavigationImplementation interface. This interface provides hooks for reconciling navigation properties, preparing option menus, handling menu item selections, calculating bar heights, and managing tab items and tab bar properties.

    Once implemented, inject your custom implementation into the ReactNavigationCoordinator singleton using injectImplementation.

    // 1. Implement the interface
    interface NavigationImplementation {
      void reconcileNavigationProperties(
          ReactInterface component,
          ReactToolbar toolbar,
          ActionBar bar,
          ReadableMap previous,
          ReadableMap next,
          boolean firstCall
      );
    
      void prepareOptionsMenu(
          ReactInterface component,
          ReactToolbar toolbar,
          ActionBar bar,
          Menu menu,
          ReadableMap previous,
          ReadableMap next
      );
    
      boolean onOptionsItemSelected(
          ReactInterface component,
          ReactToolbar toolbar,
          ActionBar bar,
          MenuItem item,
          ReadableMap config
      );
    
      float getBarHeight(
          ReactInterface component,
          ReactToolbar toolbar,
          ActionBar actionBar,
          ReadableMap config,
          boolean firstCall
      );
    
      void makeTabItem(
          ReactBottomNavigation bottomNavigation,
          Menu menu,
          int index,
          Integer itemId,
          ReadableMap config
      );
    
      void reconcileTabBarProperties(
          ReactBottomNavigation bottomNavigation,
          Menu menu,
          ReadableMap prev,
          ReadableMap next
      );
    }
    
    // 2. Inject the implementation
    NavigationImplementation implementation = new MyCustomNavigationImplementation();
    ReactNavigationCoordinator.sharedInstance.injectImplementation(implementation);
  6. Integrate Redux with Native Navigation

    master

    Because Native Navigation registers each screen as a separate root view, you can reduce Redux boilerplate by creating a custom registration wrapper. Instead of calling Navigator.registerScreen directly, you can implement a wrapper that injects the Redux Provider into every screen automatically.

    To implement this, create a helper function that takes a getStore option. This function should return a component that wraps the target screen with the <Provider store={store}> component.

    // index.js
    import registerConnectedScreen from './utils/registerConnectedScreen';
    
    registerConnectedScreen(
      'SomeScreen',
      () => require('./screens/SomeScreen'),
      {
        getStore: () => require('./path/to/redux/store'),
      },
    );
  7. Clean up React Native Libraries in Xcode

    master

    When switching to CocoaPods, you must remove the statically linked libraries provided by the React Native starter template to avoid conflicts:

    1. Open your .xcworkspace file in Xcode.
    2. In the Project navigator (left sidebar), locate the Libraries folder.
    3. Select all libraries in that folder.
    4. Right-click and select Delete.
    5. When prompted, choose Remove References.
  8. Create a new project with Native Navigation

    master

    To start a new project, use the React Native CLI. If you do not have the CLI installed, install it globally first. Then, initialize your project and install native-navigation via npm.

    IMPORTANT: Do NOT use react-native link to link native-navigation; it is currently not supported.

    npm i -g react-native-cli
    
    react-native init MyNewProject
    cd MyNewProject
    npm i --save native-navigation
  9. Initialize Native Navigation in MainApplication.java

    master

    To integrate Native Navigation into your Android application lifecycle, perform the following steps in MainApplication.java:

    1. Add NativeNavigationPackage to the getPackages() list.
    2. In the onCreate() method, initialize the ReactNavigationCoordinator with your ReactInstanceManager and call .start(this).
    import com.airbnb.android.react.navigation.NativeNavigationPackage;
    import com.airbnb.android.react.navigation.ReactNavigationCoordinator;
    
    // ... inside MainApplication class
    
    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),
          new NativeNavigationPackage()
      );
    }
    
    @Override
    public void onCreate() {
      super.onCreate();
      SoLoader.init(this, /* native exopackage */ false);
      
      // Initialize Native Navigation
      ReactNavigationCoordinator coordinator = ReactNavigationCoordinator.sharedInstance;
      coordinator.injectReactInstanceManager(mReactNativeHost.getReactInstanceManager());
      coordinator.start(this);
    }
    import com.airbnb.android.react.navigation.NativeNavigationPackage;
    import com.airbnb.android.react.navigation.ReactNavigationCoordinator;
    
    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),
          new NativeNavigationPackage()
      );
    }
    
    @Override
    public void onCreate() {
      super.onCreate();
      SoLoader.init(this, /* native exopackage */ false);
      
      ReactNavigationCoordinator coordinator = ReactNavigationCoordinator.sharedInstance;
      coordinator.injectReactInstanceManager(mReactNativeHost.getReactInstanceManager());
      coordinator.start(this);
    }