39
votes

I can't get React Native WebView postMessage to work. The React Native app successfully receives the post message sendt from the web app. But the web app does not receive messages sendt from the React Native app.

My simple web app

<html>
<body>

<button>Send post message from web</button>
<div>Post message log</div>
<textarea style="height: 50%; width: 100%;" readonly></textarea>

<script>
var log = document.querySelector("textarea");

document.querySelector("button").onclick = function() {
    console.log("Send post message");

    logMessage("Sending post message from web..");
    window.postMessage("Post message from web", "*");
}

window.addEventListener("message", function(event) {
    console.log("Received post message", event);

    logMessage(event.data);
}, false);

function logMessage(message) {
    log.append((new Date()) + " " + message + "\n");
}

</script>

</body>
</html>

This web app is hosted at: https://popping-heat-6062.firebaseapp.com/

My simple React Native app

import React, {Component} from "react";
import {AppRegistry, Text, View, TouchableHighlight, WebView} from "react-native";

export default class WevViewApp extends Component {

    constructor( props ) {
        super( props );

        this.webView = null;
    }

    onMessage( event ) {
        console.log( "On Message", event.nativeEvent.data );
    }

    sendPostMessage() {
        console.log( "Sending post message" );
        this.webView.postMessage( "Post message from react native" );
    }

    render() {
        return (
            <View style={{flex: 1}}>
                <TouchableHighlight style={{padding: 10, backgroundColor: 'blue', marginTop: 20}} onPress={() => this.sendPostMessage()}>
                    <Text style={{color: 'white'}}>Send post message from react native</Text>
                </TouchableHighlight>
                <WebView
                    style={{flex: 1}}
                    source={{uri: 'https://popping-heat-6062.firebaseapp.com'}}
                    ref={( webView ) => this.webView = webView}
                    onMessage={this.onMessage}
                />
            </View>
        );
    }
}

AppRegistry.registerComponent( 'WevViewApp', () => WevViewApp );

Expected result

  1. Clicking the "Send post message from web" button sends a post message to the React Native app and is logged in debugger window
  2. Clicking the "Send post message from React Native" button sends a post message to the web app and is printed out in the web textarea

Actual result

  1. Web app successfully sends sends message to React Native app and is logged in the debugger window
  2. React Native app unsuccessfully sends message to web app and is not printed out in the web textarea

Does anyone know why the web app is not receiving the messages?

Tested with both Android and iOS.

Related documentation: https://facebook.github.io/react-native/docs/webview.html


Edit: Experienced a behaviour that may pinpoint the problem.

When testing the 'Send post message from web' button directly in a web browser the textarea logs that it has sendt the message and also that is received it's own message.

Thu Dec 15 2016 10:20:34 GMT+0100 (CET) Sending post message from web..
Thu Dec 15 2016 10:20:34 GMT+0100 (CET) Post message from web

when trying the same in the WebView from the React Native app the textarea only prints out

Thu Dec 15 2016 10:20:34 GMT+0100 (CET) Sending post message from web..

Does the WebView hijack the window.postMessage method and inject a custom one?

This is mentioned in the documentation:

Setting this property will inject a postMessage global into your webview, but will still call pre-existing values of postMessage.

Maybe this is false and this is where the problem is at?

6
I have the same issue, spent the whole day looking for a workaround but, there ain't any, there is a pull request in process may be we get some solution. github.com/facebook/react-native/pull/10941Bhaumik Desai

6 Answers

73
votes

I had the same problem and fixed it by adding the event listener to the document instead of the window. Change:

window.addEventListener("message", ...

to:

document.addEventListener("message", ...
58
votes

Version 5 of react-native-webview changes how this behavior works. You now want:

window.ReactNativeWebView.postMessage(data);
6
votes

Add browser specific event listeners

In Android

 document.addEventListener("message", function(event) {
       alert("This is a Entry Point Working in Android");
       init(event.data)
  });

In iOS

 window.addEventListener("message", function(event) {
       alert("This is a Entry Point Working in iOS");
       init(event.data)
  });

enter image description here

0
votes

Actually the listener works differently in different OSs such as iOS and Android. So you should use document.addEventListener for Android and window.addEventListener for iOS.

if(navigator.appVersion.includes('Android')){
            document.addEventListener("message", function (data) {
               alert("you are in android OS");
            });
        }
else {
            window.addEventListener("message", function (data) {
               alert("you are in android OS");               
             });
        }
0
votes

None of the above answers will work for IOS, might still work for android devices. This way works for all platforms -

In react native:

const generateOnMessageFunction = data =>
    `(function() {
    window.WebViewBridge.onMessage(${JSON.stringify(data)});
  })()`;

class RNScreen extends React.Component{
      private webView: RefObject<WebView> = createRef();
       render(){
              return (
                    <WebView
                            javaScriptEnabled
                            domStorageEnabled
                            bounces={false}
                            ref={this.webView}
                            onLoadEnd={() => {
                                this.postMessageTest({helloFromRN: true});
                            }}
                      />
              )
       }

      private postMessageTest(data){
             this.webView.current.injectJavaScript(generateOnMessageFunction(data));
      }

}

IN JS side -

window.WebViewBridge = {
    onMessage: this._onMessage,
};
const event = new Event('WebViewBridge');
window.dispatchEvent(event);


// The data is not a string. It is an object.
private _onMessage(data: Object){
    // Should log "helloFromRN" on load.
    console.log(data)
}
0
votes

You can follow below steps for the Pub/Sub =>>

Step 1 => Add plugin in your react native app: https://github.com/react-native-webview/react-native-webview

Step 2 => Add below code in ReactNative(Mobile Part) :

import React, { Component } from "react";
import { SafeAreaView, Text, TouchableHighlight } from "react-native";
import { WebView } from "react-native-webview";

class MobileWebView extends Component {
  state = {};

  onMessage = event => {
    const { data } = event.nativeEvent;
    this.setState({ data });
  };

  render() {
    return (
      <SafeAreaView style={{ flex: 1 }}>
        <TouchableHighlight
          style={{
            padding: 10,
            backgroundColor: "red",
            width: "100%",
          }}
          onPress={() => {
            this.webview.postMessage("Message From React Native App to Web");
          }}
        >
          <Text>Send Message To Web</Text>
        </TouchableHighlight>
        <Text>Mobile: {this.state.data}</Text>
        <WebView
          ref={ref => {
            this.webview = ref;
          }}
          source={{ uri: "http://localhost:3000/" }}
          onMessage={this.onMessage}
          style={{ flex: 1 }}
        />
      </SafeAreaView>
    );
  }
}

export default MobileWebView;

Step 3 => Add below code in ReactJS(Web Part) :

import React, { Component } from "react";

class WebApp extends Component {
  state = {};
  componentDidMount() {
    document.addEventListener("message", event => {
      this.setState({ msg: event.data });
    });
    window.addEventListener("message", event => {
      this.setState({ msg: event.data });
    });
  }

  render() {
    return (
      <div>
        <div>Web: {this.state.msg}</div>

        <button
          onClick={() => {
            window.ReactNativeWebView.postMessage(
              "Post message from web to Mobile",
              "*"
            );
          }}
        >
          Send Message To React Native(Mobile)
        </button>
      </div>
    );
  }
}

export default WebApp;

And you are good to go, now you can send and receive message from Mobile to Web or vice versa.

Output Image