ReactNative: ReactNativeNavigation で画面遷移ナビゲーションを作成する

画面遷移用のコンポーネントがサードパーティから提供されているので、それを使います(公式のコンポーネントは iOS しか対応していない…なぜ…)。公式のドキュメントに「両 OS に対応しているこいつらをチェックするといいよ」と言及しているものが二つあります(つまりは有力コンポーネント)。それがこちら。

YouTube などでも挙動を公開していた後者が、UI の挙動的にも純正に近く、最終的なコードの構造的にもシンプルで(wix という会社的にも)比較的好みだったので react-native-navigation を選びました。

React Native Navigation のインストール

まずは

npm install react-native-navigation --save

react-native link hogehoge はしません。

さて、この先がそれぞれネイティブ開発環境での作業なので、一番辛いところです。(WIP)

iOS

/ios/ProjectName/ProjectName.xcodeproj を開いて、左のファイルツリーから「Libraly」を右クリックして、「」
/node_modules/ReactNativeNavigation.xcodeproj

/ios/ProjectName/AppDelegate.m を以下のように RCCManager を使うように修正します。

#import "AppDelegate.h"

#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>

#import "RCCManager.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSURL *jsCodeLocation;

  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];

//  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
//                                                      moduleName:@"ProjectName"
//                                               initialProperties:nil
//                                                   launchOptions:launchOptions];
//  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  self.window.backgroundColor = [UIColor whiteColor];
  [[RCCManager sharedInstance] initBridgeWithBundleURL:jsCodeLocation launchOptions:launchOptions];
  
  
//  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
//  UIViewController *rootViewController = [UIViewController new];
//  rootViewController.view = rootView;
//  self.window.rootViewController = rootViewController;
//  [self.window makeKeyAndVisible];
  return YES;
}

@end

Android

Android が特に情報が少ないのですが、わかる範囲でやってみると発生エラーから予想していくと、SDK バージョンを環境設定時の 23 から 25 に上げる必要があるなど、何かと読み取っていくことができるので、根気よくやりましょう。

React Native Navigation の実装

あとは簡単です。
以下のような構造であまりコード量がない感じで書くことができます。

src/components/Container.js

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

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  text: {
    color: '#fff',
    fontSize: 30,
    fontWeight: '500',
  },
});

const Container = ({ backgroundColor, onPress, children }) => (
  <View style={[styles.container, { backgroundColor }]}>
    <TouchableOpacity onPress={onPress}>
      <Text style={styles.text}>{backgroundColor}</Text>
    </TouchableOpacity>
    {children}
  </View>
);

export default Container;

src/screens/index.js

import { Navigation } from 'react-native-navigation';

import Screen1 from './Screen1';

export function registerScreens() {
  Navigation.registerComponent('screens.Screen1', () => Screen1);
}

src/screens/Screen1.js

import React, { Component } from 'react';
import { Text } from 'react-native';
import Container from '../components/Container';

class Screen extends Component {
  handlePress = () => {
    console.log('press')
  };

  render() {
    return (
      <Container
        backgroundColor="#000000"
        onPress={this.handlePress}
      />
    )
  }
}

export default Screen;

App.js

import { Navigation } from 'react-native-navigation';
import { registerScreens } from './src/screens';

registerScreens();

Navigation.startTabBasedApp({
  tabs: [
    {
      label: 'One',
      screen: 'screens.Screen1',
      icon: require('./src/assets/one.png'),
      // selectedIcon: require('../img/one_selected.png'), // iOS only
      title: 'Screen One'
    },
    {
      label: 'Two',
      screen: 'screens.Screen1',
      icon: require('./src/assets/one.png'),
      // selectedIcon: require('../img/two_selected.png'), // iOS only
      title: 'Screen Two'
    }
  ]
});