react-native-web を試す

とある API を作っいて、動作確認にしょぼいクライアントアプリを作りたくなった。

* 目標1: まずは Web で試して後から Native アプリ化したい。
* 目標2: TypeScript で書きたい。

Twitter Lite で開発されたという [react-native-web](https://github.com/necolas/react-native-web) を使ってみる。react-native-web とは、react-native に入れ替えると ReactNative アプリが HTML アプリになるという物。同一のコードを携帯でも HTML でも動かそうと思うとまだちょっと手間がかかるようだ。

## react-native-web アプリの一番簡単な作り方

react-dom の代わりに react-native-web を使うだけなら非常に簡単。create-react-app がすでに対応している。

まず、create-react-app で普通の React アプリを作る。ちなみに、npx はローカルにインストールされた node コマンドを実行するだけの物だと思っていたら、ローカルにインストールされてない時は勝手にインストールして使い終わったらアンインストールしてくれるという便利機能があった。

    npx create-react-app react-native-web-simple

react-native-web をインストール

    cd react-native-web-simple
    yarn add react-native-web

src/index.js を書き換える

```javascript
import { AppRegistry } from 'react-native';
import App from './App';

AppRegistry.registerComponent('App', () => App);

AppRegistry.runApplication('App', {
  rootTag: document.getElementById('root')
});
```

src/App.js を書き換える

```javascript
import React, { Component } from 'react';
import logo from './logo.svg';
import { Image, StyleSheet, Text, View } from 'react-native';

const Link = (props) => <Text {...props} accessibilityRole="link" style={StyleSheet.compose(styles.link, props.style)} />

class App extends Component {
  render() {
    return (
      <View style={styles.app}>
        <View style={styles.header}>
          <Image accessibilityLabel="React logo" source={logo} resizeMode="contain" style={styles.logo} />
          <Text style={styles.title}>React Native for Web</Text>
        </View>
        <Text style={styles.text}>
          This is an example of an app built
          with <Link href="https://github.com/facebook/create-react-app">Create React App</Link> and{' '}
          <Link href="https://github.com/necolas/react-native-web">React Native for Web</Link>
        </Text>
        <Text style={styles.text}>
          To get started, remix this starter kit by editing <Link href="https://glitch.com/edit/#!/react-native?path=src/App.js" style={styles.code}>src/App.js</Link>.
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  app: {
    marginHorizontal: 'auto',
    maxWidth: 500
  },
  logo: {
    height: 80
  },
  header: {
    padding: 20
  },
  title: {
    fontWeight: 'bold',
    fontSize: '1.5rem',
    marginVertical: '1em',
    textAlign: 'center'
  },
  text: {
    lineHeight: '1.5em',
    fontSize: '1.125rem',
    marginVertical: '1em',
    textAlign: 'center'
  },
  link: {
    color: '#1B95E0'
  },
  code: {
    fontFamily: 'monospace, monospace'
  }
});

export default App;
```

上記コードは <https://glitch.com/edit/#!/react-native> から拝借しました。不思議な事に react-native-web をインストールすると react-native を import 出来るようになる。これは webpack の alias という機能を使っていて、あらかじめ create-react-app の生成する webpack に alias の設定が入っているからだった。<https://github.com/facebook/create-react-app/pull/407>

## Native でも Web でも動くアプリを作る。

<https://github.com/necolas/react-native-web/blob/master/website/guides/getting-started.md#web-packaging-for-existing-react-native-apps> の通りに作業するだけですが、私は初見でかなり戸惑ったので丁寧に書きます。手順としては以下になります。

* 普通に ReactNative のプロジェクトを作る。
* JavaScript を合体させるために webpack + babel の設定を書く。
    * babel-plugin-react-native-web でソースコードで `react-native` を import すると `react-native-web` を import した事になる。
* ブラウザで表示するために index.html や index.web.js を書く。
* 微調整。

### 普通に react-native のプロジェクトを作る

    brew install node watchman yarn
    npm install -g react-native-cli
    react-native init ReactNativeTest
    cd ReactNativeTest
    react-native run-ios

### react-native-Web 化に必要なモジュールをインストール

    yarn add react-dom react-native-web
    yarn add --dev babel-plugin-react-native-web babel-loader url-loader webpack webpack-dev-server webpack-cli

### web/webpack.config.js の作成

通常の ReactNative では、Metro というツールでソースコードを合体させますが、react-native-web では webpack + babel を使います。

<https://github.com/necolas/react-native-web/blob/master/website/guides/getting-started.md#web-packaging-for-existing-react-native-apps> に書いてある通りの内容で web/webpack.config.js を作成します。

### index.html の作成

ウェブブラウザがアクセスする HTML を適当に書きます。

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>React Native Web</title>
  </head>
  <body>
    <div id="react-app"></div>
  </body>
  <script type="text/javascript" src="bundle.web.js"></script>
</html>
```

### index.web.js の作成

アプリを記述する App.js を HTML 上に設置する部分です。

index.web.js の作成

```javascript
import App from './src/App';
import { AppRegistry } from 'react-native';

// register the app
AppRegistry.registerComponent('App', () => App);

AppRegistry.runApplication('App', {
  initialProps: {},
  rootTag: document.getElementById('react-app')
});
```

### App.js の移動

`react-native init` で作ると最上位に App.js が作られますが、webpack に合わせて src に移します。

```
mkdir src
mv App.js src/App.js
```

### 実行

    ./node_modules/.bin/webpack-dev-server -d --config ./web/webpack.config.js --inline --hot --colors

ブラウザで <http://localhost:8080/> を開く。