React-native: Postmessage doesnt work in Webviews

Created on 27 Feb 2018  ·  13Comments  ·  Source: facebook/react-native

Is this a bug report?

Yes

Have you read the Contributing Guidelines?

Yes

Environment

Environment:
OS: macOS High Sierra 10.13.3
Node: 8.9.3
Yarn: 1.1.0
npm: 5.5.1
Watchman: 4.9.0
Xcode: Xcode 9.2 Build version 9C40b
Android Studio: 1.4 AI-141.2343393

Packages: (wanted => installed)
react: 16.2.0 => 16.2.0
react-native: 0.53.3 => 0.53.3

Steps to Reproduce

  1. Init a new app using: react-native init
  2. Put the provided code in the bottom of this page into App.js
  3. Start the app by running react-native run-android
  4. Try to send a message from the HTML page in the webview using window.postmessage (loaded webpage has a button that sends it)

Expected Behavior

I would expect to see a console log "On message" in my android using 'adb logcat' when triggering the window.postmessage from within the webview.

Actual Behavior

Nothing displays in the log when triggering window.postmessage. Communication the other way from the app into the webview using injected javascript works ok.

Reproducible Demo

App code to be pasted into App.js after running "react-native init":

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 );

The code for the webview html can be found at: https://popping-heat-6062.firebaseapp.com
This code was based on a stackoverflow question: https://stackoverflow.com/questions/41160221/react-native-webview-postmessage-does-not-work

Locked

Most helpful comment

I've investigated and found a workaround for my issue. Foundings below:

First off: In the webpage I linked to from the stackoverflow post there was a bug, it used document.postmessage instead of window.postmessage.

In my tests I tried to trigger the postmessage command onload. This failed. Once I added a timeout before posting it works ok. You can add the following to your test to illustrate the issue:

document.addEventListener('DOMContentLoaded', function () {
window.postMessage('Without timeout'); // Doesnt work
setTimeout(() => window.postMessage('With timeout'), 5000); // Works
}, false)

All 13 comments

This behavior is already covered in the tests, which are currently passing. See WebViewExample.js.

Can you dig a little further and figure out why your particular use case is failing?

Thanks for pointing me to the tests. I'll review the tests, examine it a bit more and see if I can narrow it down more.

I've investigated and found a workaround for my issue. Foundings below:

First off: In the webpage I linked to from the stackoverflow post there was a bug, it used document.postmessage instead of window.postmessage.

In my tests I tried to trigger the postmessage command onload. This failed. Once I added a timeout before posting it works ok. You can add the following to your test to illustrate the issue:

document.addEventListener('DOMContentLoaded', function () {
window.postMessage('Without timeout'); // Doesnt work
setTimeout(() => window.postMessage('With timeout'), 5000); // Works
}, false)

Just leaving a note, I was able to get this to work without needing DOMContentLoaded or a long timeout. This worked:setTimeout(() => window.postMessage('With timeout'), 0)

Hi all,
I am working on react Native webview, I try to post some message to onMessage() function in Webview props. It's work very well in some of the android devices and some of it's not working. I am not able to identify what the issue can you please help me? please check the below code @staffannase @hramos @ratbeard @mojodna @jsierles

// calling from HTML cide
 setTimeout(() => postMessage('Calling from HTML'), 1000);   

// WebView components
<WebView 
    source={{ html: concatHTML, baseUrl: 'web/' }}
    javaScriptEnabled={true}
    javaScriptEnabled={true}                    
    domStorageEnabled={true}        
    scalesPageToFit={true}
    scrollEnabled={false}
    automaticallyAdjustContentInsets={true}
    style={{ height: 390, width: 390, justifyContent: 'center' }} 
    onMessage={m => console.warn(m.nativeEvent.data)} />


I am facing the same issue on iOS and rn 0.57.2 ..onMessage does not logs any message./
Note same is working on android

@viv3kk can you please share your code here? , so we can look at into that

_onMessage = event => {
this.props.getTitle(event.nativeEvent.data.message) // event.nativeEvent: null
}
render() {
return (
renderError={this.renderError}
{...this.props}
useWebKit={true}
onShouldStartLoadWithRequest={this.startLoadWithRequest}
injectedJavaScript={this.injectedJS}
onMessage={this._onMessage}
decelerationRate="normal"
/>
)
}
@ap050492 me too, the same issue on IOS and rn 0.57.2 , onMessage response the event.nativeEvent
null , so i cant take anything from onMessage

after try all the way, i found using "postMessage" directly on RN0.56,works well so simple on both iOS & Android!

  1. without “window. or document.”
  2. without “ injectedJavaScript”
  3. without “timeout”
  4. without “originWhitelist”

just one important thing is make sure the “HTML side code” has nothing errors or warnings in browser !
and the only problem is there is a yellow box "Error: Unable to open URL: about:blank" , i hadn't solved it yet !

html side:
// send message to RN
image

// get message from RN
image

RN side:
image

// get message from webview
image

// send message to webview
image

Hi, I am using Highcharts to plot charts in a webview. On Clicking on certain points on the chart i need to get data from each clicked point in the RN code. I run the following code in the click event of highcharts

click: function (event) { 
                            console.log('[On Click]', window);
                            postMessage(JSON.stringify({
                                name: this.name,
                                x: event.point.category,
                                y: event.point.y
                            }));
                        }

On the RN side,

onMessage(event) {
        console.log('[RN][On Message] start')
        debugger;
        console.log("On Message", event.nativeEvent.data);
    }

<WebView
                    ref={webview => { this.webview = webview; }}
                    onLayout={this.reRenderWebView}
                    style={styles.full}
                    source={{ html: concatHTML, baseUrl: 'web/' }}
                    javaScriptEnabled={true}
                    domStorageEnabled={true}
                    scalesPageToFit={true}
                    scrollEnabled={false}
                    automaticallyAdjustContentInsets={true}
                    onMessage={this.onMessage.bind(this)}
                    {...this.props}
                />

Firstly, on debugging WebView, the following code returns error

postMessage(JSON.stringify({
                                name: this.name,
                                x: event.point.category,
                                y: event.point.y
                            }));

returns error
Uncaught TypeError: Failed to execute 'postMessage' on 'Window': 2 arguments required, but only 1 present.

to fix this i had to do the following

postMessage(JSON.stringify({
                                name: this.name,
                                x: event.point.category,
                                y: event.point.y
                            }), '*');

By doing this I dont get the error anymore, but onMessage={this.onMessage.bind(this)} never gets fired. I dont see an error on the webview nor on the RN side. Not sure what am I doing wrong.
Please help

@ws7one
DO NOT use local html resource, there are lots of problems I tried, especially on android release apk!
put the local html file on to the server end!

I found some solution for this problem. Just, i inject following script into webview:

const script = `
function postMessage(message) {
  var hash = window.location.hash;
  window.location.hash='message=' + message;
  window.location.hash=hash;
}

//Now just call following function
postMessage('Any message');
`;

After i create component:

export default class SomeWebComponent extends Component {
  handleNavigation = (event) => {
    const url = event.url;
    const sections = url.split('#');
    if(sections.length > 1 && sections[1].indexOf('message=') != -1) {
       const message = sections[1[.replace('message=', '');
       //Do any action with message
       return;
    }
    //Other navigation actions
  }

  render() {
   return (
     <WebView
       source={anySource}
       injectedJavaScript={script}
       javaScriptEnabled = {true}
       onNavigationStateChange = {this.handleNavigation}
     />
   );
  }
}

I hope what it may help you))

@Raserad great idea .... but it didnt work here with expo 32. What I did to solve the issue, inspired in your idea, was to use window.location and did the same thing. But, in my case, I couldnt use postMessage, since it wasnt working on expo. I had to implement a custom onShouldStartLoadWithRequest in order to not go to my "message urls".

Was this page helpful?
0 / 5 - 0 ratings