react-native-navigation
Version:
React Native Navigation - truly native navigation for iOS and Android
302 lines (238 loc) • 8.7 kB
Markdown
# 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
└── processors/ # Extensibility hooks
```
## Core Classes
### NavigationDelegate (Public API)
**File**: `NavigationDelegate.ts`
Facade providing the public `Navigation` API. All methods proxy to `NavigationRoot`.
```typescript
// 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)
```typescript
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
```typescript
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.
```typescript
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:
```typescript
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 |
## Processors Layer
### OptionProcessorsStore
**File**: `processors/OptionProcessorsStore.ts`
Registers custom option processors:
```typescript
Navigation.addOptionProcessor('topBar.title.text', (value, commandName) => {
return value.toUpperCase(); // Transform all titles
});
```
### LayoutProcessorsStore
**File**: `processors/LayoutProcessorsStore.ts`
Registers layout transformers:
```typescript
Navigation.addLayoutProcessor((layout, commandName) => {
// Add default options to all components
return layout;
});
```
## Interfaces
### Layout Types
**File**: `interfaces/Layout.ts`
```typescript
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:
```typescript
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