【ReactNative】WebViewを使って既存資産を使い回す

# はじめに
`ReactNative`を使って既存Webサービスのアプリ版を開発している場合、一部のアプリ独自の機能(例えばプッシュ通知やカメラ)を除いては既存ソースを流用できる場合があります。
例でいうと、***ユーザ情報画面は既存のWebのマイページをそのまま表示する***とかです。
その場合、`WebView`を使うことになると思います。
`ReactNative`でいうところの`WebView`は`react-native-webview`を使うことになりますが、ここで問題になってくるのが`ReactNative`側のコードと`WebView`側のコードの連携処理です。
先のマイページの例でいうと、`WebView`側のマイページ画面のソースコードに`ReactNative`側からユーザ情報を渡してあげなければ成立しません。
今回は`react-native-webview`を導入した上で、`WebView`との値の受け渡し方法について調べました。


# 環境情報
```
"react-native": "0.61.5",
"react-native-webview": "^9.2.1",
```

# インストール
`react-native-webview`をインストールしていない場合はインストール。

```
yarn add  react-native-webview
```

# WebViewに値を渡す
`WebView`に値を渡す場合は`injectedJavaScript`を使います。
`injectedJavaScript`には文字列型で`Javascript`のコードを渡せます。

```javascript:WebViewComponent.tsx
import React from 'react';
import { WebView } from 'react-native-webview';

// WebViewに渡すコード
const injectedCode : string = `
    function fireInjectedJavaScript(){
        alert('ReactNativeから渡されたコードを実行しました!');
    }
`;

// WebViewで表示させるHTML
const html : string = `
      <html>
      <head>
      </head>
      <body>
        <h1>Title</h1>
        <form name="test">
            <input type="button" value="injectedJavaScript" onClick="fireInjectedJavaScript()">
        </form>
      </body>
      </html>
`;

// ...割愛

render(
    <WebView source={{html : html}} injectedJavaScript={injectedCode} />
);
```

`html`中のボタンで`fireInjectedJavascript()`を実行していますが、これは`ReactNative`側から渡された関数です。
よって、アプリで表示された場合のみ特定の処理を行うような作りにすることもできます。

渡したコードは`WebView`が描画された後に実行されるため、`jQuery`や`document.getElementById`なんかを使って`WebView`側の特定のフォームの値や表示をいじることもできます(上記のマイページの例ではこの方法を使ってユーザ情報を渡す)。

# WebViewから値を貰う
逆に`WebView`側から何らかのデータを受け取りたい場合は`onMessage`を使います。

```javascript:WebViewComponent2.tsx
import React from 'react';
import { WebView, WebViewMessageEvent } from 'react-native-webview';

// WebViewで表示させるHTML
const html : string = `
      <html>
      <head>
        <script>
            // WebViewが読み込まれた時にデータを送信
            window.onload = function() {
                window.ReactNativeWebView.postMessage("onload")
            };

            // 定期的に実行(ポーリングなんかに使える？？)
            setInterval(function () {
                window.ReactNativeWebView.postMessage({hoge : "setInterval"})
            }, 2000)

            // 特定の処理実行時にデータを送信
            function firePostMessage(){
                window.ReactNativeWebView.postMessage("button click")
            }
        </script>
      </head>
      <body>
        <h1>Title</h1>
        
        <form name="test">
            <input type="button" value="postMessage" onClick="firePostMessage()">
        </form>
      </body>
      </html>
`;

// ...割愛

// onMessage発火時処理
public onMessageFromHtml(event : WebViewMessageEvent) {
    console.log(event.nativeEvent.data);
}

render(
    <WebView source={{html : html}} onMessage={onMessageFromHtml} />
);
```
上記の例では`WebView`読み込み時に実行と、定期的に実行、ボタンを押した時に実行、と３パターンを盛り込んでいます。
いずれの場合も`window.ReactNativeWebView.postMessage`を実行しており、`ReactNative`側では`WebViewMessageEvent`型で受け取ります。
渡された値自体は`WebViewMessageEvent`の`nativeEvent.data`の中に格納されています。
`nativeEvent.data`は`react`の`BaseSyntheticEvent`のジェネリクスとなっているため型は自由です(setIntervalのようにオブジェクトを渡すこともできます)。

# おわりに
今回は`react-natiev-webview`をインストールし、`ReactNative`と`WebView`間でのデータの受け渡し方法を紹介しました。
既存のソースを使いまわせるので非常に有効な手ではないかと思います。