React Native の WebView 内外でやりとりする
最近 React Native をやり始めた erukiti です。ビギナーです。優しくしてください。
諸々の事情から WebView で構成されたコンテンツを描画することになりました。
WebView は、ウェブフロントエンド的にわかりやすく言えば、IFrame のような技術です。WebView
コンポーネントの中では、静的な HTML か、どこかのサイトのウェブページが内部にレンダリングされます。
Android Native なら同名の WebView があり、iOS/macOS なら、WkWebView があります。
今回は、React Native における WebView で、WebView の中から外、あるいはその逆にやりとりをするための方法についてまとめました。
React Native の WebView
React Native の WebView は以前のバージョンだと組み込みだったものが、今は独立したパッケージになってるようですね。
- https://github.com/react-native-webview/react-native-webview
- https://github.com/react-native-webview/react-native-webview/blob/master/docs/Getting-Started.md
- https://github.com/react-native-webview/react-native-webview/blob/master/docs/Reference.md
さて今回の記事では WebView で、内から外、外から内への通信を行う方法です。
ちょうど、ドキュメントにCommunicating between JS and Native というそのものズバリなものがあります。
- React Native WebView のプロパティで JS を指定する
injectedJavaScript
- React Native WebView の ref から JS を指定する
injectJavaScript
- WebView の中身からメッセージを送信する
window.ReactNativeWebView.postMessage
と React Native WebView のプロパティでメッセージを受信するonMessage
この 3 つを使ってやりとりをすることになります。
injectedJavaScript
パッケージ公式の https://github.com/react-native-webview/react-native-webview/blob/master/docs/Guide.md#the-injectedjavascript-prop はクラスコンポーネントにおける説明です。関数型コンポーネント(Hooks)で書くとこんな感じでしょうか。
const App: React.FC = () => {
return (
<WebView
style={{ flex: 1 }}
source={{
url: 'http://localhost:3000',
}}
injectedJavaScript={`
alert('Injected!');
true; // 必須
`}
/>
)
}
injectedJavaScrpt
は文字列を JavaScript として実行するプロパティです。
-
console.log
はどこに出るか不明なのでたぶん使えない - 末尾の
true
がなぜか必要note: this is required, or you'll sometimes get silent failures
実行タイミングによって、
- injectedJavaScriptBeforeContentLoaded
- injectedJavaScriptForMainFrameOnly
- injectedJavaScriptBeforeContentLoadedForMainFrameOnly
という亜種もあります。
基本的には、ページを読み込む前にグローバルに影響を及ぼすコードを走らせるものです。
injectJavaScript
初期化時点でグローバルに JS を実行するだけだとあまりに不便です。そこで WebView の ref からメソッドで実行する injectJavaScript
というものも用意されています。
const App: React.FC = () => {
const ref = React.useRef<any>()
React.useEffect(() => {
setTimeout(() => {
ref.injectJavaScript(`
alert('inject!');
true;
`)
}, 3000)
}, [])
return (
<WebView
ref={ref}
style={{ flex: 1 }}
source={{
url: 'http://localhost:3000',
}}
/>
)
}
型は適当。
ref.injectJavaScript(code)
で任意のタイミングでコードを実行可能です。Button
を押したらコードを実行する、生体認証が OK ならコードを実行する、のような処理が可能です。
postMessage / onMessage
WebView 内部で実行されてるコードの方から、React Native のアプリへ働きかけたいときに使える方法も用意されています。
window.ReactNativeWebView.postMessage(message)
です。ただし、message
は文字列でなければいけません。複数のデータ、複雑なデータ構造などを送りたい場合は JSON なりにすると良いでしょう。
postMessage
で送信された文字列は onMessage
プロパティで受け取ることができます。
// React Native 側
const App: React.FC = () => {
return (
<WebView
style={{ flex: 1 }}
source={{
url: 'http://localhost:3000'
}}
onMessage={(ev) => console.log(ev.nativeEvent.data)}
>
)
}
// WebView 内部のコード
if ('ReactNativeWebView' in window) {
window.ReactNativeWebView.postMessage(
JSON.stringify({ type: 'hoge', hoge: 'ほげ' }),
)
}
のように使います。
postMessage
なのに、送信だけしかできず返信を受け取ることが出来ないなど、使い勝手が良いとは言えませんが、一応 WebView 内部から通信が可能です。
まとめ
- 初期化につかえる
injectedJavaScript
プロパティ(同系統のものが残り三つある) - ワンショットできる
injectJavaScript
メソッド - WebView 内部から送信できる
postMessage
関数
統一感が無いですが、これらを組み合わせて使うしかないようです。
React Native 初心者なので、間違っているかもしれません。間違いや、こうしたほうがいい、みたいなものがあればぜひコメントなどを書いていただけると助かります。
Discussion