WebエンジニアがReact Nativeを使った感想

## React Native
- JavaScriptでネイティブアプリを作れる。
- (ほぼ)共通のコードでiOSとAndroid両方のアプリを作れる、クロスプラットフォームなもの。
- Webエンジニアの学習コストが低いらしい。

---

## 環境構築
- Xcode8より高いバージョンが必要であり、シュミレーターやちょっとした細かい設定などで使うことがある。
- SwiftやObjective-Cで書いたコードを呼び出すこともできる。
- Genymotionなど入れておく。
- 下記のものなどをインストールする。（使わないものもあり）

``` lang:インストール諸々
npm install -g create-react-native-app
brew tap caskroom/cask
brew cask install android-sdk
brew install node
brew install watchman
npm install -g react-native-cli
```

Xcodeを起動してPreferences -> LocationsからCommand Line Toolsを設定する。 <br>
<img width="737" alt="Screen Shot 2017-06-26 at 8.38.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/93753/0e339dc1-cffc-87f1-6252-8a4b18759981.png">


---

## 起動まで

``` lang:terminal
create-react-native-app CountApp
cd CountApp
npm start
```

- 0.44まで`react-native init AppName`でアプリケーションを作れたが、0.45から`create-react-native-app AppName`で作れるようになった。
- react-native-cliで`react-native run-ios`や`react-native run-android`でシミュレータを起動できたが、***npm startだけでiOSもAndroidも起動できる***ようになった。（両方同時にシミュレータで動作させることはまだできなさそう）
- npm start後、シミュレータを立ち上げて、流れに沿うと下記の画面へ遷移できる。

<img width="386" alt="Screen Shot 2017-06-29 at 9.13.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/93753/28c93520-7ba4-6225-e84d-e2334b403292.png">

---

## 簡単なアプリを作ってみる
ボタンがクリックされたら数字が増えるアプリの開発をします。
下記は現在最新の0.45をベースで、手順は下記の通り実装しました。

1. ボタンの配置
2. デザイン周り

---

## ボタンの配置

Buttonというコンポーネントでボタンを作れます。onPressでクリックされた際に呼び出す関数を指定できます。

``` lang:CountApp/App.js
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';

export default class App extends React.Component {
  oneCountUp(number) {
    console.log(number);
  }
  render() {
    return (
      <View style={styles.container}>
        <Button
          onPress={this.oneCountUp.bind(this, 1)}
          title="1"
          />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

```

---

結果は下記の通り。

<img width="369" alt="Screen Shot 2017-06-29 at 23.09.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/93753/a15323cf-fabb-af23-e4ac-9099303c84ac.png">

---

また、シュミレータ上で`⌘D`を押し、ブラウザでログの出力を確認しましょう。

<img width="1491" alt="Screen Shot 2017-06-29 at 9.43.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/93753/5058dd1f-f832-f65f-bd6c-8e4ac7745f8b.png">

---

### stateで数字の管理
onPress()で数字渡さないで、stateで管理します。constructorやcomponentWillMountだったりで初期化して、数字を上げていきます。
ついでにボタンも増やしたものが下記のコードです。

``` lang:CountApp/App.js
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';

export default class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      number: 0
    }
  }
  oneCountUp() {
    this.setState({
      number: this.state.number + 1
    })
  }
  twoCountUp() {
    this.setState({
      number: this.state.number + 2
    })
  }
  fiveCountUp() {
    this.setState({
      number: this.state.number + 5
    })
  }
  tenCountUp() {
    this.setState({
      number: this.state.number + 10
    })
  }
  render() {
    return (
      <View style={styles.container}>
        <Text>{ this.state.number }</Text>
        <Button
          onPress={this.oneCountUp.bind(this)}
          title="1"
        />
        <Button
          onPress={this.twoCountUp.bind(this)}
          title="2"
        />
        <Button
          onPress={this.fiveCountUp.bind(this)}
          title="5"
        />
        <Button
          onPress={this.tenCountUp.bind(this)}
          title="10"
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});
```

---

結果の画面は下記の通りです。
ボタンをクリックすると実際に数字が増えます。

<img width="383" alt="Screen Shot 2017-06-29 at 23.24.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/93753/ef684169-811d-47c2-84c0-d360e618aa18.png">

---

## デザイン周り
今の状態だとデザインがイケてないので、デザイン周りの実装をします。
ReactNativeは、CSSライクにデザインを実装できます。

### デザインの仕方
下記の二種類の書き方があります。

1. コンポーネントで直接デザインを実装する方法
2. ReactNativeのStyleSheetを使って実装する方法


``` lang:デザインの実装例
render() {
    return (
      <View style={styles.container}>
        <Text style={{ color: '#f00' }}>コンポーネントで直接デザインを実装する方法</Text>
        <Text style={ styles.sampleText }>ReactNativeのStyleSheetを使って実装する方法</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1
  },
  sampleText: {
    color: '#00f'
  }
});
```

---

### アプリケーションで実装する
ReactNativeのStyleSheetを使う方法で今回は実装します。

``` lang:デザインの実装例
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';

export default class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      number: 0
    }
  }
  oneCountUp() {
    this.setState({
      number: this.state.number + 1
    })
  }
  twoCountUp() {
    this.setState({
      number: this.state.number + 2
    })
  }
  fiveCountUp() {
    this.setState({
      number: this.state.number + 5
    })
  }
  tenCountUp() {
    this.setState({
      number: this.state.number + 10
    })
  }
  render() {
    return (
      <View style={ styles.container }>
        <View style={ styles.numberField }>
          <Text style={ styles.number }>{ this.state.number }</Text>
        </View>
        <View style={ styles.btnField }>
          <Button
            onPress={ this.oneCountUp.bind(this) }
            style={ styles.numberBtn }
            title="1"
          />
          <Button
            onPress={ this.twoCountUp.bind(this) }
            style={ styles.numberBtn }
            title="2"
          />
          <Button
            onPress={ this.fiveCountUp.bind(this) }
            style={ styles.numberBtn }
            title="5"
          />
          <Button
            onPress={ this.tenCountUp.bind(this) }
            style={ styles.numberBtn }
            title="10"
          />
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  numberField: {
    width: '100%',
    flex: 4,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#162228'
  },
  number: {
    color: '#fff'
  },
  btnField: {
    width: '100%',
    flex: 1,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#17272f'
  },
  numberBtn: {
    color: '#fff'
  }
});
```

---

結果は下記の通り。

<img width="367" alt="Screen Shot 2017-06-30 at 0.08.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/93753/22bdc9eb-3670-fead-c481-89481c5e75ce.png">

---

numberBtnで指定しているはずなのに、ボタンの色が変わりません。これは、Buttonの コンポーネントにtextStyleを渡してあげないといけないからです。
けどstylesにまとめてるのに、汚くなりますよね。そのような場合、TouchableOpacityなどを使っても良いでしょう。
TouchableOpacityを使って、ちょっとした修正をしたのが下記のコードです。

``` lang:デザインの実装例
import React from 'react';
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
import Dimensions from 'Dimensions';

export default class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      number: 0
    }
  }
  oneCountUp() {
    this.setState({
      number: this.state.number + 1
    })
  }
  twoCountUp() {
    this.setState({
      number: this.state.number + 2
    })
  }
  fiveCountUp() {
    this.setState({
      number: this.state.number + 5
    })
  }
  tenCountUp() {
    this.setState({
      number: this.state.number + 10
    })
  }
  render() {
    return (
      <View style={ styles.container }>
        <View style={ styles.numberField }>
          <Text style={ styles.number }>{ this.state.number }</Text>
        </View>
        <View style={ styles.btnField }>
          <TouchableOpacity
            onPress={ this.oneCountUp.bind(this) }
            style={ styles.numberBtn }
          >
            <Text style={ styles.numberBtnText }>1</Text>
          </TouchableOpacity>
          <TouchableOpacity
            onPress={ this.twoCountUp.bind(this) }
            style={ styles.numberBtn }
          >
            <Text style={ styles.numberBtnText }>2</Text>
          </TouchableOpacity>
          <TouchableOpacity
            onPress={ this.fiveCountUp.bind(this) }
            style={ styles.numberBtn }
          >
            <Text style={ styles.numberBtnText }>5</Text>
          </TouchableOpacity>
          <TouchableOpacity
            onPress={ this.tenCountUp.bind(this) }
            style={ styles.numberBtn }
          >
            <Text style={ styles.numberBtnText }>10</Text>
          </TouchableOpacity>
        </View>
      </View>
    );
  }
}

const windowWidth = Dimensions.get('window').width;
const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  numberField: {
    width: '100%',
    flex: 5,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#162228'
  },
  number: {
    color: '#fff',
    fontSize: 40
  },
  btnField: {
    width: '100%',
    flex: 1,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center'
  },
  numberBtn: {
    width: windowWidth / 4,
    height: '100%',
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#17272f'
  },
  numberBtnText: {
    color: '#fff'
  }
});
```

---

結果

<img width="369" alt="Screen Shot 2017-06-30 at 0.25.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/93753/595ee6aa-d96e-fd4b-20ba-8694fcd35e37.png">

---

## まとめ
- デザインし易い。なんとなく馴染みがある感じに実装できる。
- JS・React詳しくなくても、そこそこ実装できる。
- 今回の記事では取り上げなかったが、PhotoライブラリのPrivacyの設定など、多少はネイティブの知識は必要になってくる。