1
0

[ReactNative] WebViewとアプリでデータをやりとりする

Posted at

前提

WebViewで表示している画面の操作をアプリでハンドリングしたい

ネイティブアプリケーションを開発していると、どうしてもWebViewを使用せざるを得ないことって多くないでしょうか?例えば、ECのアプリで決済周りの部分がAPI開発すると色々面倒なので、WebViewでそのまま表示させるとか、スクラッチで作ると工数がかかるから一部分はWebViewにする...などなど。
そんな時に、「WebView内でのイベントをキャッチしてアプリ側でハンドリングしたい」なんてことがしばしばあるかと思います。(モーダル出すとか、アプリの画面に遷移させるとか)
今回はそんな時に、どのようにしてやりとりを行うか説明していきます。

流れ

  1. react-native-webview<WebView />コンポーネントのinjectedJavaScript propsでWebView内で実行したいJavaScriptを記述。
  2. そのJavaScriptの中でwindow.ReactNativeWebView.postMessage()する。
  3. アプリ側ではonMessage={} propsでWebView内からのpostMessageを受け取る。
  4. onMessageでアプリでやりたい処理を記述

実装

完成系は以下です。

SampleScreen.tsx
import React from 'react';
import { StyleSheet, View } from 'react-native';
import WebView, { WebViewMessageEvent } from 'react-native-webview';

export const SampleScreen = () => {
  const injectedJavaScript = `
    window.ReactNativeWebView.postMessage(
      JSON.stringify({
        type: 'postTest',
      })
    );
  `;

  const _onMessage = async (event: WebViewMessageEvent) => {
    const data = JSON.parse(event.nativeEvent.data);
    switch (data.type) {
      case 'postTest':
        console.log('受け取りました。');
        break;
      default:
        break;
    }
  };

  return (
    <View style={styles.container}>
      <WebView
        style={styles.container}
        containerStyle={styles.container}
        source={{ uri: 'https://example.com/' }}
        injectedJavaScript={injectedJavaScript}
        onMessage={_onMessage}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});

解説

WebViewコンポーネントのinjectedJavaScript propsにはWebView内で実行したいJavaScriptを渡してあげます。今回はwindow.ReactNativeWebView.postMessage()としてtypeプロパティにpostTestという文字列を渡してあげます。

injectedJavaScript.js
const injectedJavaScript = `
    window.ReactNativeWebView.postMessage(
      JSON.stringify({
        type: 'postTest',
      })
    );
  `;

このinjectedJavaScriptはWebViewコンポーネントが読み込まれた時に実行されます。もし、実行するタイミングをずらしたいのであれば、domcontentloadedmutationObserversetTimeout、クリックイベントなら.addEventListener('click', function(event) {});などを使うと良いでしょう。

そして、このpostMessageをアプリ側で受け取るためにonMessage={_onMessage}として、定義しています。

onMessage.js
const _onMessage = async (event: WebViewMessageEvent) => {
    const data = JSON.parse(event.nativeEvent.data);
    switch (data.type) {
      case 'postTest':
        console.log('受け取りました。');
        break;
      default:
        break;
    }
  };

引数にeventを受け取ることができて、event.nativeEvent.dataでpostMessageしたデータを受け取ることができます。今回はtype: 'postTest',なので、event.nativeEvent.data.typeで'postTest'を受け取ることができます。他にも

injectedJavaScript.js
const injectedJavaScript = `
    window.ReactNativeWebView.postMessage(
      JSON.stringify({
        type: 'postTest',
        id: 1
      })
    );
  `;

とすればevent.nativeEvent.data.idとしてデータを受け取ることができます。今回のようにswitch-case句で記述すると、他にもinjectedScriptでpostMessageした場合にもハンドリングすることができます。

最後にcase 'postTest'の中で実行したい処理を記述します。コンポーネントをレンダリングしたり、stateを更新したりできます。

まとめ

いかがだったでしょうか。個人的にはブラウザでDOM操作していることになるので、ロード時に実行したいJavaScriptがある場合はローディングを挟んだ方がいいかなと思いました。あと、injectScript内で処理が失敗すると、後続の処理はもちろん実行されなくなるので、注意が必要です。

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0