React NativeとWebViewでいつでも双方向通信する
最近仕事で React を使うようになりました。
で、React Native アプリ(以下 RN)と WebView(以下 WV)の双方向でメッセージをやり取りしたかった。
公式ドキュメントの奥深く、Communicating between JS and Nativeに書いてある。
というわけで、いつでも双方向通信を実現してみた。
動作確認
- react-native: 0.63.2
- react-native-webview: 10.8.3
ReactNative 側のソース
react-native
のWebView
はディスコンらしい。
ので react-native-webview
のWebView
を使います。
source
プロパティで Web ページを埋め込むのですが、localhost
じゃ動きません。
アドレスを指定しましょう。動作検証するためにはこのアドレスが RN 側の端末で解決できる必要があります。
import React, { useState } from 'react'
import { StyleSheet, View, Text, Button } from 'react-native'
import { WebView } from 'react-native-webview'
function App(props) {
const [msg, setMessage] = useState('NO HANDLE MESSAGE')
const [webref, setWebref] = useState()
// WebViewからのメッセージを受信
function onMessage(message) {
setMessage(message.nativeEvent.data)
}
// WebViewにメッセージを送信
let count = 1
function postMessage() {
webref.postMessage(`HELLO JS!! ${count++}`)
}
return (
<View style={{ flex: 1 }}>
<WebView
ref={setWebref}
source={{ uri: 'http://192.168.11.15:3000/' }} // localhostじゃ動かないので
onMessage={onMessage}
/>
<Text style={styles.floatText}>{msg}</Text>
<Button onPress={postMessage} title="POST MESSAGE" />
</View>
)
}
const styles = StyleSheet.create({
floatText: {
top: -300,
color: 'white',
},
})
WebView からメッセージを受け取る
WV からメッセージを受け取るため、WebView
のonMessaage
プロパティにハンドラonMessage
関数を仕掛けます。
WebView にメッセージを送信する
WebView
コンポーネントのpostMessage
メソッドを通じて送信します。
WebView
への参照が必要なので、ref
プロパティでwebref
State に参照を保持します。
ボタンを押したらpostMessage
関数で WV 側にメッセージを送信します。
WebView 側のソース
import React, { useState } from 'react'
import './App.css'
function App() {
const [msg, setMessage] = useState('INIT.')
// RNにメッセージを送る
let count = 1
function postMessage(msg) {
if (window.ReactNativeWebView) {
// 存在チェック
window.ReactNativeWebView.postMessage(`${msg} ${count++}`)
}
}
// RNからメッセージを受け取る
if (window.ReactNativeWebView) {
window.addEventListener('message', (msg) => {
setMessage(msg?.data)
})
}
return (
<div className="App">
<header className="App-header">
<div>{msg}</div>
<button onClick={() => postMessage('HELLO? NATIVE!!')}>
POST MESSAGE
</button>
</header>
</div>
)
}
export default App
ReactNative からメッセージを受け取る
window
のmessage
イベントで受け取ります。
※ 初出時、 document.addEventListner
で動作確認しましたが、今日確認したらwindow.addEventListener
でした。なぜ・・・?
ReactNative にメッセージを送信する
以前はwindow.postMessage
だったらしいですが、現在はwindow.ReactNativeWebView.postMessage
を使えとのこと。
RN を通さずにローカルでサイトを表示するとwindow.ReactNativeWebView
が無くて落ちるので、存在チェックを入れます。
できた!
WV 側のリスナーがwindow
なのかdocument
なのかでハマりましたが、無事両方にメッセージを送信することができました!
文字列の送信のみなので、オブジェクトの送受信をしたければJSON.stringify
とJSON.parse
を駆使することになると思います。
React
が書ければネイティブアプリが作れるなんて。React
すげぇな!
(vue も好きだけど)これだけでVue.js
から乗り換える価値あり。
参考サイト
Communicating between JS and Native
https://github.com/react-native-community/react-native-webview/blob/master/docs/Guide.md#controlling-navigation-state-changes