React Native 入門してみた② 画面遷移編

[React Native 入門してみた① 環境構築編+HelloWorld](https://qiita.com/garbanzo/items/abd892a12aae2cc19530)につづいて今回は画面遷移についての入門です。

# 画面遷移
画面遷移出来ないと話にならないので学習。
候補として下記ライブラリがありましたが、GithubStar数が多く扱いやすそうなどの理由から`react-navigation`を画面遷移を実装します。

- [wix/react-native-navigation: A complete native navigation solution for React Native](https://github.com/wix/react-native-navigation)
- [aksonov/react-native-router-flux: The first declarative React Native router](https://github.com/aksonov/react-native-router-flux)
- [react-navigation/react-navigation: Routing and navigation for your React Native apps](https://github.com/react-navigation/react-navigation)

構成は前回の記事同様、TypeScript構成です。
実装には、下記サイトを参考にさせて頂きました。

- [【React Native】React Navigation を使ってみる（画面遷移編） - Qiita](https://qiita.com/keneo/items/b62ddcab0e35ce792b67)


## インストール
React Navigationをインストールします。
`react-native-gesture-handler`も必要なようなので一緒にインストールします。

```
 npm install --save react-navigation@2.0.1 react-native-gesture-handler
```
何故`@2.0.1`なのかは下記、**問題1. ビルドエラー**を参考下さい。

## 実装

### 構造

```
├── App.tsx
├── app.json
├── assets
│   ├── icon.png
│   └── splash.png
├── babel.config.js
├── package-lock.json
├── package.json
├── src
│   ├── navigator
│   │   └── index.ts
│   └── screens
│       ├── Main.tsx
│       ├── Modal.tsx
│       └── Push.tsx
└── tsconfig.json
```
`src/navigator/index.ts`に画面の遷移処理を記載します。
`screens/`配下のコンポーネントが各画面です。


### ナビゲーションの実装

navigator/index.ts

```index.ts
import { createStackNavigator, NavigationScreenProp } from 'react-navigation'

//画面
import Main from '../screens/Main'
import Push from '../screens/Push'
import Modal from '../screens/Modal'

export interface HomeScreenProps {
  navigation: NavigationScreenProp<any, any>
};

export const MainNavigation = createStackNavigator(
  {
    Main: { screen: Main },
    Push: { screen: Push },
  },
  { initialRouteName: 'Main', mode: 'card', headerMode: 'none' }
)

export default createStackNavigator(
  {
    MainNavigation: { screen: MainNavigation },
    Modal: { screen: Modal },
  },
  { initialRouteName: 'MainNavigation', mode: 'modal', headerMode: 'none' },
)
```
ここでは各画面の遷移処理を記載します。
画面は`screens/`配下に作成します。
`createStacknavigator`を使用して画面遷移を作成します。
`initialRouteName`で初期画面を設定し、画面遷移の方法は`mode`を指定して変更します。
modeはレンダリングと遷移のスタイルを定義します。
`modal`・`card`の種類が存在しそれぞれ下記になります。

 - modal: 標準のiOSとAndroidの画面遷移を使用します。 これがデフォルトです。
 - card: 画面を下からスライドさせます。iOSでのみ動作し、Androidでは動作しません。

続いて、各画面の実装します
screens/Main.tsx

```Main.tsx
import React, {Component} from 'react';
import { Container, View, Header, Left, Body, Right, Button, Title, Text } from 'native-base';

import {HomeScreenProps} from '../navigator'

export default class Main extends Component<HomeScreenProps> {
  push = () => {
    const { navigation } = this.props
    navigation.navigate('Push')
  }
  modal = () => {
    const { navigation } = this.props
    navigation.navigate('Modal')
  }
  render() {
    return (
      <Container>
        <Header>
          <Left />
          <Body>
            <Title>メイン</Title>
          </Body>
          <Right />
        </Header>
        <View>
          <Button small iconRight  transparent primary onPress={this.push}>
            <Text>プッシュ表示</Text>
          </Button>
        </View>
        <View>
          <Button small iconRight  transparent primary onPress={this.modal}>
            <Text>モーダル表示</Text>
          </Button>
        </View>
      </Container>
    );
  }
}
```
メイン画面の実装です。
`this.props.navigation.navigate`を使用することで画面遷移出来ます。
`.navigate('Push')`でPush画面に遷移、PushをModalとすることで、Modal画面に遷移出来ます。

screens/PUsh.tsx

```Push.tsx
import React, {Component} from 'react';
import { Container, Header, Left, Body, Right, Button, Icon, Title } from 'native-base';

import {HomeScreenProps} from '../navigator'

export default class Push extends Component<HomeScreenProps> {
  render() {
    return (
      <Container>
        <Header>
          <Left>
            <Button transparent onPress={() => this.props.navigation.goBack()}>
              <Icon name='arrow-back' />
            </Button>
          </Left>
          <Body>
            <Title>プッシュ</Title>
          </Body>
          <Right />
        </Header>
      </Container>
    );
  }
}
```
`this.props.navigation.goBack()`で前の画面に戻ることが出来ます。

App.tsx

```App.tsx
import React, { Component } from 'react';
import Navigator from './src/navigator';

export default class App extends Component {
  render() {
    return (
      <Navigator />
    );
  }
}
```

ルート(親)コンポーネントでは、`<Navigaror/> (src/navigator/index.ts)`コンポーネントをrenderします。


## ハマったポイント
実装中にハマったポイントです。

### 問題1. ビルドエラー

```
Tried to register two views with the same name RNGestureHandlerButton

register
    ReactNativeViewConfigRegistry.js:76:4
```
- [Tried to register two views with the same name RNGestureHandlerButton – React Native Navigation – Knoll Labs](http://knolllabs.com/tried-to-register-two-views-with-the-same-name-rngesturehandlerbutton-react-native-navigation/)
上記によるとVerseion3系で発生するようで2にダウングレードすると問題なく動きます
`"react-navigation": "^2.0.1"`

### 問題2. TypeScript対応時の各ページのPropsについて

```
type Props = {}
export default class Main extends Component<Props> {
...
```
として場合、` this.props.navigation.navigate('Push')`でエラーとなってします。
そこで、react-navigationからinterfaceを作成する

```
import { createStackNavigator,NavigationScreenProp} from 'react-navigation'

export interface HomeScreenProps {
  navigation: NavigationScreenProp<any,any>
}
```

上記interfaceを各画面のPropsに追加することで対応できます
 [typescript - Add strong typing for react navigation props - Stack Overflow](https://stackoverflow.com/questions/47924501/add-strong-typing-for-react-navigation-props?rq=1)


## Next 
続いては、[React Native 入門してみた③  Httpアクセス]()です。