【ReactNative】WebViewを使ってWebコンテンツを表示する

6 min読了の目安(約5900字TECH技術記事

はじめに

スマホアプリを開発すると一口にいっても、その背景には色々あると思います。
個人開発なら「学習も兼ねてTODOリスト等の開発しやすいものを作ってみる」という場合もあるでしょうし、もう少し規模の大きな話になってくると「既存のWEBサービスのスマホ版を出したい」といった場合もあるかと思います。

特に後者の場合ですと、ゼロからスマホアプリを作るというよりは「今WEBにある内容」をスマホに最適化した形で落とし込む(+プッシュ通知等のスマホ固有の機能を付ける)というアプローチの開発だと思います。

WEB側がレスポンシブな場合「あれ、これそのまま使えるのでは?」といった機能や画面もあるかと思います。
分かりやすいところだと、QA画面や利用規約画面あたりはスマホアプリでもブラウザでも同じ見栄えで良さそうです。

今回はReactNativeで開発しているアプリにおいて、react-native-webviewを使うことで既に存在するWebソースをそのまま流用する方法を紹介したいと思います。

そもそもWebViewとは

ReactNativeに限らずWebViewという言葉を聞いたことがある方は多いかと思います。
WebViewとはスマホアプリ上においてブラウザのようにWebコンテンツを表示させる機能です。

アプリによっては殆どの画面をWebViewで表示させているものもあれば、ピンポイントでWebリソースを使っている例もあり、用途は様々です。

前提条件

  • react@16.13.1
  • react-native@0.63.3
    Typescriptを使用していきます。

インストール

yarn add react-native-webview

さらにiosの方はpod installが必要です。

cd ios && pod install

基本編「コンテンツの表示」

まずは基本となるコンテンツの表示方法について紹介します。
HTMLを表示させるわけですが、方法は2通りあります。

①HTMLを文字列で渡す

一番オーソドックスな使い方はHTMLコンテンツを直接文字列で渡す方法です。
下記の例ではstring型で定義した簡単なHTMLコンテンツを表示させています。

import React from 'react';
import { SafeAreaView, StyleSheet, StatusBar} from 'react-native';
import { WebView } from 'react-native-webview';

// WebViewで表示させるHTML
const html : string = `
  <html>
    <head></head>
    <body>
      <h1 style="font-size:56;color:red;" >Title</h1>
      <p style="font-size:44px;color:blue;font-weight:bold;" >This page is html.</p>
    </body>
  </html>
`;

const App: React.FC = () => {
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView style={{flex: 1}}>
        <WebView source={{html: html}} />
      </SafeAreaView>
    </>
  );
};

const styles = StyleSheet.create({});

export default App;

出力

スタイルの指定まで効いていることが分かります。

webview1

②URI指定

冒頭で述べた例では、こちらのパターンを利用するケースが多いと思います。
既にあるWebコンテンツを表示する場合はuriとして指定します。

下記の例では、Zennのトップページを指定しています。

import React from 'react';
import { SafeAreaView, StyleSheet, StatusBar} from 'react-native';
import { WebView } from 'react-native-webview';

const App: React.FC = () => {
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView style={{flex: 1}}>
        <WebView source={{uri: "https://zenn.dev"}} />
      </SafeAreaView>
    </>
  );
};

const styles = StyleSheet.create({});

export default App;

出力

Zennはレスポンシブデザインのため、WebViewで表示しても崩れることなく表示されています。
そのままスマホアプリとして使用しても遜色ないUIですね。

webview2

応用編「データの授受」

ここまでで基本となるコンテンツの表示はできるようになりました。
しかしあくまで表示ができているだけで、スマホアプリ側のデータの授受ができていません。

例えば「WebView側でボタンを押した時にReactNative側で定義した関数を発火させる」ことや「WebView側で行った処理結果をReactNative側が受けとる」等を行いたい場合があると思います。

①「injectedJavaScriptでJSをWeb側に渡す」

WebViewinjectedJavascriptというパラメータを指定することで、ReactNative側で定義したJSWebコンテンツ側に渡すことができます。

ボタンを押した時の関数を定義して渡したい場合は、コンテンツより先にJSが読み込まれる必要があるためinjectedJavaScriptBeforeContentLoadedを指定します。

import React from 'react';
import { SafeAreaView, StyleSheet, StatusBar} from 'react-native';
import { WebView } from 'react-native-webview';

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

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

const App: React.FC = () => {
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView style={{flex: 1}}>
        <WebView 
          source={{html: html}} 
          injectedJavaScriptBeforeContentLoaded={injectedCode} />
      </SafeAreaView>
    </>
  );
};

const styles = StyleSheet.create({});

export default App;

出力

表示されたボタンをタップすることで、ReactNative側から渡された関数が実行されることが確認できます。

webview3

②「onMessageでJSからReactNativeへ値を渡す」

Webコンテンツ側から何らかの値をReactNativeに返してあげたい場合はonMessageにイベントハンドラを指定します。
イベントで取得できる値はWebViewMessageEventという発火イベント情報もろもろを内包した型です。実際に渡された値はnativeEvent.dataの中に格納されています。

その上でWebコンテンツ側ではwindow.ReactNativeWebView.postMessage()を実行し、渡したい値を引数に指定します。

import React from 'react';
import { SafeAreaView, StyleSheet, StatusBar} from 'react-native';
import { WebView, WebViewMessageEvent } from 'react-native-webview';

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

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

// WebView側からのデータ取得イベント
const onMessageFromHtml = (event : WebViewMessageEvent) => {
  // event.nativeEvent.dataの中に値が入っている
  console.log(event.nativeEvent.data);
}

const App: React.FC = () => {
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView style={{flex: 1}}>
        <WebView 
          source={{html: html}} 
          injectedJavaScriptBeforeContentLoaded={injectedCode}
          onMessage={onMessageFromHtml} />
      </SafeAreaView>
    </>
  );
};

const styles = StyleSheet.create({});

export default App;

出力

画面に表示されたボタンを押すことで、Webコンテンツ側からReactNativeに値が送られてログに表示されました。

LOG ReactNativeから渡されたコードを実行しました!

まとめ

今回はReactNativeのアプリ上でWebコンテンツを表示するライブラリであるreact-native-webviewについて紹介しました。
作りたいアプリの内容によっては殆どWebコンテンツで賄えることもあると思います。
使い回しができる箇所はどんどん使っていくことで、開発コストを削減する効果も見込めるため非常にオススメです。

また、アプリの冒頭に表示させるキャンペーン画面等もWebViewで作成しておくと、キャンペーンが切り替わる毎にアプリのアップデートを行いストアへ公開する必要もなくなるので、そういった場面でも有用なのではないかと思います。