Skip to content

Latest commit

 

History

History
331 lines (261 loc) · 10.5 KB

File metadata and controls

331 lines (261 loc) · 10.5 KB

JavaScript/TypeScript Architecture

The JavaScript layer provides the public API and orchestrates communication between React components and native navigation implementations.

Entry Point

index.ts exports the Navigation singleton (a NavigationDelegate instance) along with re-exports from Modal, EventsRegistry, Constants, and all interfaces. Applications use Navigation.* for commands and Navigation.events().* for event subscriptions.

Module Structure

src/
├── index.ts                 # Main entry point, exports Navigation singleton
├── Navigation.ts            # NavigationRoot - core orchestrator
├── NavigationDelegate.ts    # Public API facade
├── adapters/                # Native bridge layer
├── commands/                # Command processing
├── components/              # React component management
├── events/                  # Event system
├── interfaces/              # TypeScript type definitions
├── linking/                 # Deep-linking framework (URL → command)
└── processors/              # Extensibility hooks

Core Classes

NavigationDelegate (Public API)

File: NavigationDelegate.ts

Facade providing the public Navigation API. All methods proxy to NavigationRoot.

// Usage
import { Navigation } from 'react-native-navigation';
Navigation.setRoot({ root: { stack: { children: [...] } } });

NavigationRoot (Core Orchestrator)

File: Navigation.ts

Coordinates all subsystems. Constructed with dependencies:

  • NativeCommandsSender - sends commands to native
  • NativeEventsReceiver - receives events from native
  • AppRegistryService - manages React Native component registry

Initializes and coordinates:

  • Store - component instance & props storage
  • ComponentRegistry - component registration
  • LayoutTreeParser - converts layouts to internal nodes
  • LayoutTreeCrawler - processes layout trees
  • Commands - command execution
  • EventsRegistry - event subscriptions
  • OptionsProcessor - option processing
  • LayoutProcessor - layout transformations

Adapters Layer

Bridge between JavaScript and native/React Native APIs.

Adapter Purpose
NativeCommandsSender Wraps TurboModule for command dispatch
NativeEventsReceiver Wraps NativeEventEmitter for events
NativeRNNTurboModule TurboModule spec interface
NativeRNNTurboEventEmitter TurboModule event emitter spec
UniqueIdProvider Generates unique component/command IDs
ColorService Converts colors to native format
AssetResolver Resolves require() image assets (class: AssetService)
AppRegistryService Registers components with React Native
Constants Platform dimension constants
TouchablePreview 3D Touch / Haptic preview handling

Commands Layer

Commands Class

File: commands/Commands.ts

Central command dispatcher. Each navigation command:

  1. Validates input
  2. Processes layout through pipeline
  3. Sends to native via adapter
  4. Returns promise with result

Processing Pipeline

The pipeline executes in this exact order:

Input Layout (from API)
       ↓
1. OptionsCrawler.crawl()         # Extract static options from component classes
       ↓
2. LayoutProcessor.process()      # Apply registered layout processors
       ↓
3. LayoutTreeParser.parse()       # Convert to internal LayoutNode tree
       ↓
4. LayoutTreeCrawler.crawl()      # Process options, save props to Store
       ↓
5. OptionsProcessor (during crawl) # Resolve colors, assets, custom processors
       ↓
6. NativeCommandsSender           # Send to native module

LayoutNode (Internal Tree)

interface LayoutNode {
  id: string;           // Unique identifier
  type: LayoutType;     // Component|Stack|BottomTabs|etc
  data: {
    name?: string;      // Component name
    options?: any;      // Processed options
    passProps?: any;    // Component props (cleared before native)
  };
  children: LayoutNode[];
}

LayoutType Enum

enum LayoutType {
  Component = 'Component',
  Stack = 'Stack',
  BottomTabs = 'BottomTabs',
  SideMenuRoot = 'SideMenuRoot',
  SideMenuCenter = 'SideMenuCenter',
  SideMenuLeft = 'SideMenuLeft',
  SideMenuRight = 'SideMenuRight',
  TopTabs = 'TopTabs',
  ExternalComponent = 'ExternalComponent',
  SplitView = 'SplitView',
}

Components Layer

ComponentRegistry

File: components/ComponentRegistry.ts

Manages React component registration and wrapping.

Navigation.registerComponent('ScreenName', () => MyComponent);

ComponentWrapper

File: components/ComponentWrapper.tsx

Higher-order component that:

  • Wraps original component with lifecycle management
  • Stores instance in Store for event dispatch
  • Injects componentId and componentName props

Store

File: components/Store.ts

Centralized storage for:

  • componentsByName - registered component providers
  • componentsInstancesById - mounted component instances
  • propsById - static props by component ID
  • pendingPropsById - props awaiting component mount
  • wrappedComponents - cached wrapped components
  • lazyRegistratorFn - function for lazy component registration

Events Layer

EventsRegistry

File: events/EventsRegistry.ts

Public API for event subscriptions:

Navigation.events().registerComponentDidAppearListener(({ componentId }) => {});
Navigation.events().registerCommandCompletedListener(({ commandId }) => {});

ComponentEventsObserver

File: events/ComponentEventsObserver.ts

Dispatches events to component instance methods:

  • componentWillAppear()
  • componentDidAppear()
  • componentDidDisappear()
  • navigationButtonPressed()
  • screenPopped()

CommandsObserver

File: events/CommandsObserver.ts

Notifies listeners of command lifecycle (start, complete).

Events (Native → JS)

Event Description
RNN.AppLaunched App initialization complete
RNN.ComponentWillAppear Component about to appear
RNN.ComponentDidAppear Component now visible
RNN.ComponentDidDisappear Component hidden
RNN.NavigationButtonPressed TopBar button tapped
RNN.BottomTabPressed Tab pressed (even if selected)
RNN.BottomTabSelected Tab selection changed
RNN.BottomTabLongPressed Tab long-pressed
RNN.ModalDismissed Modal was dismissed
RNN.ModalAttemptedToDismiss Swipe-to-dismiss attempted
RNN.ScreenPopped Screen removed from stack
RNN.SearchBarUpdated Search text changed
RNN.SearchBarCancelPressed Search cancelled
RNN.PreviewCompleted 3D Touch preview completed
RNN.CommandCompleted Navigation command finished

Linking Layer

Implements URL-to-screen routing on top of the standard command pipeline. The whole feature is JS-only; it consumes URLs from RN's Linking module and dispatches showModal (or a user-supplied command) when a match is found.

Public surface (proxied through NavigationDelegate):

  • Navigation.setLinking(config) — configure prefixes, screen map, and customization hooks
  • Navigation.handleDeepLink(url) — manually feed a URL (push notifications, branch.io, App Clips)
  • Navigation.setLinkingReady(ready) — user-controlled gate for deferred deep links

Internal modules (src/linking/):

File Purpose
LinkingHandler.ts Orchestrator. Subscribes to Linking.url/getInitialURL, drives the queue, dispatches matches. Instantiated by NavigationRoot.
URLParser.ts Strips prefix (longest match), removes fragment, decodes path segments + query params.
RouteMatcher.ts Compiles ScreensConfig into a RouteNode tree; matches paths to screen chains with parameter extraction.
DeferredLinkQueue.ts FIFO queue. Holds URLs until both gates open (root-ready + user-ready).
ModalLayoutBuilder.ts Default presentation: wraps the matched chain in a stack, merges params into passProps, filters React-reserved keys (ref, key).
types.ts Public types: LinkingConfig, RouteMatch, ScreensConfig, etc. (re-exported via src/index.ts).

Readiness gates — a URL is dispatched only when both are true:

  1. Root-ready (automatic): the first Navigation.setRoot() has resolved. NavigationRoot.setRoot calls linkingHandler.setRootReady() after the promise resolves.
  2. User-ready (optional): config.isReady() returns true, or setLinkingReady(true) was called.

Resolution priority when a match is found:

  1. config.onLink(match) — full escape hatch, RNN does nothing else
  2. config.getModal(match) — custom modal layout
  3. Default ModalLayoutBuilder output

Processors Layer

OptionProcessorsStore

File: processors/OptionProcessorsStore.ts

Registers custom option processors:

Navigation.addOptionProcessor('topBar.title.text', (value, commandName) => {
  return value.toUpperCase(); // Transform all titles
});

LayoutProcessorsStore

File: processors/LayoutProcessorsStore.ts

Registers layout transformers:

Navigation.addLayoutProcessor((layout, commandName) => {
  // Add default options to all components
  return layout;
});

Interfaces

Layout Types

File: interfaces/Layout.ts

interface LayoutRoot {
  root: Layout;
  modals?: any[];
  overlays?: any[];
}

type Layout = {
  component?: LayoutComponent;
  stack?: LayoutStack;
  bottomTabs?: LayoutBottomTabs;
  sideMenu?: LayoutSideMenu;
  splitView?: LayoutSplitView;
  topTabs?: LayoutTopTabs;
  externalComponent?: ExternalComponent;
}

Options Interface

File: interfaces/Options.ts (comprehensive)

Contains all configuration options for:

  • Status bar, navigation bars
  • Top bar (buttons, title, search)
  • Bottom tabs
  • Side menus
  • Modals, overlays
  • Animations
  • Layout parameters

NavigationComponent

File: interfaces/NavigationComponent.ts

Base class for navigation-aware components:

class MyScreen extends NavigationComponent<Props> {
  static options: Options = { ... };

  componentDidAppear(event) { }
  navigationButtonPressed(event) { }
}

Dependencies

Production:

  • lodash - utility functions
  • hoist-non-react-statics - HOC support
  • react-lifecycles-compat - lifecycle polyfill
  • tslib - TypeScript helpers

Peer:

  • react, react-native (required)
  • remx (optional state management)

Build Output

Uses react-native-builder-bob:

  • Source: src/
  • Output: lib/
  • Targets: ESM modules + TypeScript declarations