New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

react native html postMessage can not reach to WebView #11594

Closed
beiming opened this Issue Dec 22, 2016 · 46 comments

Comments

Projects
None yet
@beiming

beiming commented Dec 22, 2016

I use React Native webview to show index.html, and HTML will post messge to the app. The app will then receive the message and write it to console. The problem is the app cannot receive messages, when postMessage is immediately run on head. I think it maybe related to HTML not finished loading. I then used a delay with setTimeout, and it worked.

Now I want to know:

  • Is there better way to solve this problem?
  • Why the delay 100 milliscond did not work, but delay 200 milliscond did?

I am using React Native version 0.39.0, and Node version 7.2.0.

Here is the code I have so far:

index.html

<head>
<title>Index</title>
<script type="text/javascript" src="index.js"></script>
<script type="text/javascript">
    // can not be received
    postMessage('send to react native from index inline, no delay', '*');

    // can not be received
    setTimeout(function(){
        postMessage('send to react native from index inline, delay 0 milliscond', '*')
    }, 0);

    // can received
    setTimeout(function(){
        postMessage('send to react native from index inline, delay 100 milliscond', '*')
    }, 100);
    
    onload = function() {
        // can not be received
        postMessage('send to react native from index inline after onload, no delay', '*')

        // can received
        setTimeout(function () {
            postMessage('send to react native from index inline after onload, delay 0 milliscond', '*')
        }, 0);
    };
</script>

index.js

// can not be received
postMessage('send to react native from index.js, no delay', '*');

// can not be received
setTimeout(function() {
    postMessage('send to react native from index.js, delay 100 milliscond', '*')
}, 100);

// can received
setTimeout(function() {
    postMessage('send to react native from index.js, delay 200 milliscond', '*')
}, 200);

React Native web_view_page.js

return (
        <WebView
            source={{uri: 'http://127.0.0.1:8000/index.html'}}
            onMessage={(event) => console.log('onMessage:', event.nativeEvent.data)}/>
    );

Chrome console log

2016-12-21 11:45:02.367 web_view.js:147 onMessage: send to react native from index inline after onload, delay 0 milliscond
2016-12-21 11:45:02.491 web_view.js:147 onMessage: send to react native from index inline, delay 100 milliscond
2016-12-21 11:45:02.628 web_view.js:147 onMessage: send to react native from index.js, delay 200 milliscond
@anttu

This comment has been minimized.

Show comment
Hide comment
@anttu

anttu Jan 8, 2017

Without setTimeout() I'm getting:

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

However it does seem to work with the delay.

anttu commented Jan 8, 2017

Without setTimeout() I'm getting:

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

However it does seem to work with the delay.

@farazs

This comment has been minimized.

Show comment
Hide comment
@farazs

farazs Jan 11, 2017

Contributor

Having the same issue. If a message is sent right when the page is loaded and javascript is executed it fails with that error. However, if it's executed later it seems to work. Any update on this?

Contributor

farazs commented Jan 11, 2017

Having the same issue. If a message is sent right when the page is loaded and javascript is executed it fails with that error. However, if it's executed later it seems to work. Any update on this?

@Dryymoon

This comment has been minimized.

Show comment
Hide comment
@Dryymoon

Dryymoon Jan 24, 2017

You can wait for RN postMessage bridge ready, by running this function once before any window.postMessage call in yours code, tested on RN 0.39

function awaitPostMessage() {
  let isReactNativePostMessageReady = !!window.originalPostMessage;
  const queue = [];
  let currentPostMessageFn = function store(message) {
    if (queue.length > 100) queue.shift();
    queue.push(message);
  };
  if (!isReactNativePostMessageReady) {
    // const originalPostMessage = window.postMessage;
    Object.defineProperty(window, 'postMessage', {
      configurable: true,
      enumerable: true,
      get() {
        return currentPostMessageFn;
      },
      set(fn) {
        currentPostMessageFn = fn;
        isReactNativePostMessageReady = true;
        setTimeout(sendQueue, 0);
      }
    });
  }

  function sendQueue() {
    while (queue.length > 0) window.postMessage(queue.shift());
  }
}

Usage:

awaitPostMessage(); // Call this only once in your Web Code.

/* After you can call window.postMessage many times, and as soon as RN Message Bridge is ready - messadges delivered */
window.postMessage('Once :) ');
window.postMessage('Twice :) ');

Notice: In my code i limit queue for 100 message, fell free to increase this, or remove limit at all.

Dryymoon commented Jan 24, 2017

You can wait for RN postMessage bridge ready, by running this function once before any window.postMessage call in yours code, tested on RN 0.39

function awaitPostMessage() {
  let isReactNativePostMessageReady = !!window.originalPostMessage;
  const queue = [];
  let currentPostMessageFn = function store(message) {
    if (queue.length > 100) queue.shift();
    queue.push(message);
  };
  if (!isReactNativePostMessageReady) {
    // const originalPostMessage = window.postMessage;
    Object.defineProperty(window, 'postMessage', {
      configurable: true,
      enumerable: true,
      get() {
        return currentPostMessageFn;
      },
      set(fn) {
        currentPostMessageFn = fn;
        isReactNativePostMessageReady = true;
        setTimeout(sendQueue, 0);
      }
    });
  }

  function sendQueue() {
    while (queue.length > 0) window.postMessage(queue.shift());
  }
}

Usage:

awaitPostMessage(); // Call this only once in your Web Code.

/* After you can call window.postMessage many times, and as soon as RN Message Bridge is ready - messadges delivered */
window.postMessage('Once :) ');
window.postMessage('Twice :) ');

Notice: In my code i limit queue for 100 message, fell free to increase this, or remove limit at all.

@sylvainbaronnet

This comment has been minimized.

Show comment
Hide comment
@sylvainbaronnet

sylvainbaronnet Jan 27, 2017

Thanks @Dryymoon, Do you know if it works in Android too ?

sylvainbaronnet commented Jan 27, 2017

Thanks @Dryymoon, Do you know if it works in Android too ?

@artdevgame

This comment has been minimized.

Show comment
Hide comment
@artdevgame

artdevgame Jan 29, 2017

@Dryymoon Thanks for your solution. Do you know if this is intended behaviour? Seems a little odd to have to wait on the bridge to be ready to use before calling it.

I would expect the queueing behaviour to be taken care of automatically by react-native.

artdevgame commented Jan 29, 2017

@Dryymoon Thanks for your solution. Do you know if this is intended behaviour? Seems a little odd to have to wait on the bridge to be ready to use before calling it.

I would expect the queueing behaviour to be taken care of automatically by react-native.

@jqn

This comment has been minimized.

Show comment
Hide comment
@jqn

jqn Jan 31, 2017

@beiming In my case I wanted to trigger postMessage on click and I wasn't able to trigger the message until I added event.perventDefault() in my click event like this in the WebView page:

  $(".my-btn").on("click", function(event){
    event.preventDefault();
    postMessage("send message");
  });

Also I'm able to trigger postMessage by wrapping it with:

$(document).ready(function(){
   postMessage("ready");
});

jqn commented Jan 31, 2017

@beiming In my case I wanted to trigger postMessage on click and I wasn't able to trigger the message until I added event.perventDefault() in my click event like this in the WebView page:

  $(".my-btn").on("click", function(event){
    event.preventDefault();
    postMessage("send message");
  });

Also I'm able to trigger postMessage by wrapping it with:

$(document).ready(function(){
   postMessage("ready");
});
@gp3gp3gp3

This comment has been minimized.

Show comment
Hide comment
@gp3gp3gp3

gp3gp3gp3 Feb 8, 2017

@beiming Thanks for this workaround. Setting the 0 millisecond timeout on window.onload works in iOS but doesn't seem to work on android unless I set it to 200.

This still feels flimsy though, as I'm banking on I'm guessing the native bridge to be good to go at 200 milliseconds? I don't really know why this behavior is happening, but setting timeouts doesn't feel right.

gp3gp3gp3 commented Feb 8, 2017

@beiming Thanks for this workaround. Setting the 0 millisecond timeout on window.onload works in iOS but doesn't seem to work on android unless I set it to 200.

This still feels flimsy though, as I'm banking on I'm guessing the native bridge to be good to go at 200 milliseconds? I don't really know why this behavior is happening, but setting timeouts doesn't feel right.

@andreibarabas

This comment has been minimized.

Show comment
Hide comment
@andreibarabas

andreibarabas Feb 21, 2017

guys, this works on Android (i haven't tested it on iOS yet).

  <html>
   <body>
   <script>

   function waitForBridge() {
      
       //the react native postMessage has only 1 parameter
       //while the default one has 2, so check the signature
       //of the function
       
       if (window.postMessage.length !== 1){
         
         setTimeout(waitForBridge, 200);
       }
       else {
         
         window.postMessage('abc');
       }
    }
    
    window.onload = waitForBridge;
    </script>
    </body>
    </html>

andreibarabas commented Feb 21, 2017

guys, this works on Android (i haven't tested it on iOS yet).

  <html>
   <body>
   <script>

   function waitForBridge() {
      
       //the react native postMessage has only 1 parameter
       //while the default one has 2, so check the signature
       //of the function
       
       if (window.postMessage.length !== 1){
         
         setTimeout(waitForBridge, 200);
       }
       else {
         
         window.postMessage('abc');
       }
    }
    
    window.onload = waitForBridge;
    </script>
    </body>
    </html>
@farazs

This comment has been minimized.

Show comment
Hide comment
@farazs

farazs Mar 1, 2017

Contributor

I think for Android you can use window.__REACT_WEB_VIEW_BRIDGE.postMessage instead.

the delay for Android is in executing this:

window.postMessage = function(data) {
  __REACT_WEB_VIEW_BRIDGE.postMessage(String(data));
};

You can use the javascript interface directly instead for Android based on the implementation. However, this could break in a future version if they update it.

Contributor

farazs commented Mar 1, 2017

I think for Android you can use window.__REACT_WEB_VIEW_BRIDGE.postMessage instead.

the delay for Android is in executing this:

window.postMessage = function(data) {
  __REACT_WEB_VIEW_BRIDGE.postMessage(String(data));
};

You can use the javascript interface directly instead for Android based on the implementation. However, this could break in a future version if they update it.

@chuyik

This comment has been minimized.

Show comment
Hide comment
@chuyik

chuyik Mar 2, 2017

@farazs Man you really saved my day.

chuyik commented Mar 2, 2017

@farazs Man you really saved my day.

@doxiaodong

This comment has been minimized.

Show comment
Hide comment
@doxiaodong

doxiaodong Mar 15, 2017

@Dryymoon' s method is good, but in some env, you'd better use es5 code:

    function awaitPostMessage() {
      var isReactNativePostMessageReady = false;
      var queue = [];
      var currentPostMessageFn = function store(message) {
        queue.push(message);
      };
      if (!isReactNativePostMessageReady) {
        Object.defineProperty(window, "postMessage", {
          configurable: true,
          enumerable: true,
          get() {
            return currentPostMessageFn;
          },
          set(fn) {
            currentPostMessageFn = fn;
            isReactNativePostMessageReady = true;
            setTimeout(sendQueue, 0);
          }
        });
      }

      function sendQueue() {
        while (queue.length > 0) window.postMessage(queue.shift());
      }
    }
    awaitPostMessage();

doxiaodong commented Mar 15, 2017

@Dryymoon' s method is good, but in some env, you'd better use es5 code:

    function awaitPostMessage() {
      var isReactNativePostMessageReady = false;
      var queue = [];
      var currentPostMessageFn = function store(message) {
        queue.push(message);
      };
      if (!isReactNativePostMessageReady) {
        Object.defineProperty(window, "postMessage", {
          configurable: true,
          enumerable: true,
          get() {
            return currentPostMessageFn;
          },
          set(fn) {
            currentPostMessageFn = fn;
            isReactNativePostMessageReady = true;
            setTimeout(sendQueue, 0);
          }
        });
      }

      function sendQueue() {
        while (queue.length > 0) window.postMessage(queue.shift());
      }
    }
    awaitPostMessage();
@farazs

This comment has been minimized.

Show comment
Hide comment
@farazs

farazs Apr 11, 2017

Contributor

@Dryymoon I'm curious why the setTimeout is needed? Once the set is called we update the postMessage and it should work right? It seems not to work without it but I'm not sure why.

Contributor

farazs commented Apr 11, 2017

@Dryymoon I'm curious why the setTimeout is needed? Once the set is called we update the postMessage and it should work right? It seems not to work without it but I'm not sure why.

@kyle-ilantzis

This comment has been minimized.

Show comment
Hide comment
@kyle-ilantzis

kyle-ilantzis Apr 12, 2017

For Android, Dryymoon's solution works when using the debug version. However, the release version causes the error "__REACT_WEB_VIEW_BRIDGE.postMessage is not a function"

kyle-ilantzis commented Apr 12, 2017

For Android, Dryymoon's solution works when using the debug version. However, the release version causes the error "__REACT_WEB_VIEW_BRIDGE.postMessage is not a function"

@chriscohoat

This comment has been minimized.

Show comment
Hide comment
@chriscohoat

chriscohoat Apr 21, 2017

@doxiaodong I think you need to replace the get/set portion with the following:

...
        get: function () {
          return currentPostMessageFn;
        },
        set: function (fn) {
          currentPostMessageFn = fn;
          isReactNativePostMessageReady = true;
          setTimeout(sendQueue, 0);
        }
...

Otherwise Webstorm is telling me that ECMAScript 5.1 doesn't support shorthand generator methods .

chriscohoat commented Apr 21, 2017

@doxiaodong I think you need to replace the get/set portion with the following:

...
        get: function () {
          return currentPostMessageFn;
        },
        set: function (fn) {
          currentPostMessageFn = fn;
          isReactNativePostMessageReady = true;
          setTimeout(sendQueue, 0);
        }
...

Otherwise Webstorm is telling me that ECMAScript 5.1 doesn't support shorthand generator methods .

@shenlq

This comment has been minimized.

Show comment
Hide comment
@shenlq

shenlq Apr 27, 2017

For Android, the release version causes the error "__REACT_WEB_VIEW_BRIDGE.postMessage is not a function"!!!

shenlq commented Apr 27, 2017

For Android, the release version causes the error "__REACT_WEB_VIEW_BRIDGE.postMessage is not a function"!!!

@kyle-ilantzis

This comment has been minimized.

Show comment
Hide comment
@kyle-ilantzis

kyle-ilantzis Apr 27, 2017

@shenlq if you disable proguard then it should work again

In android/app/build.gradle

def enableProguardInReleaseBuilds = false

kyle-ilantzis commented Apr 27, 2017

@shenlq if you disable proguard then it should work again

In android/app/build.gradle

def enableProguardInReleaseBuilds = false

@AshokICreate

This comment has been minimized.

Show comment
Hide comment
@AshokICreate

AshokICreate May 2, 2017

@Dryymoon

code works but now i am getting below error. any idea?

"Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined"

AshokICreate commented May 2, 2017

@Dryymoon

code works but now i am getting below error. any idea?

"Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined"

@pcstl

This comment has been minimized.

Show comment
Hide comment
@pcstl

pcstl May 2, 2017

I had the same issue here. I managed to "solve it" as my code needs to be ran only when the user changes page, so I can bind it to window.onbeforeunload, but there definitely needs to be a canonical way to check if the WebView Bridge is ready.

pcstl commented May 2, 2017

I had the same issue here. I managed to "solve it" as my code needs to be ran only when the user changes page, so I can bind it to window.onbeforeunload, but there definitely needs to be a canonical way to check if the WebView Bridge is ready.

@farazs

This comment has been minimized.

Show comment
Hide comment
@farazs

farazs May 2, 2017

Contributor

For Android there's the __REACT_WEB_VIEW_BRIDGE workaround I mentioned.

For iOS I ended up using https://github.com/CRAlpha/react-native-wkwebview and it doesn't have this issue with the bridge.

UIWebView also has a lot of issues that are fixed in WKWebView.

Hope this helps.

Contributor

farazs commented May 2, 2017

For Android there's the __REACT_WEB_VIEW_BRIDGE workaround I mentioned.

For iOS I ended up using https://github.com/CRAlpha/react-native-wkwebview and it doesn't have this issue with the bridge.

UIWebView also has a lot of issues that are fixed in WKWebView.

Hope this helps.

@AshokICreate

This comment has been minimized.

Show comment
Hide comment
@AshokICreate

AshokICreate May 3, 2017

@pcstl

could you please provide examples?

AshokICreate commented May 3, 2017

@pcstl

could you please provide examples?

@joenoon

This comment has been minimized.

Show comment
Hide comment
@joenoon

joenoon May 3, 2017

Contributor

I ran into "Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined" the other day using the awaitPostMessage trick.

What ended up working for me ended up being pretty simple:

  1. Give your js an entrypoint function like main()
  2. Add a ref to your WebView
  3. in componentDidMount: this.webView.injectJavaScript('main()')
Contributor

joenoon commented May 3, 2017

I ran into "Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined" the other day using the awaitPostMessage trick.

What ended up working for me ended up being pretty simple:

  1. Give your js an entrypoint function like main()
  2. Add a ref to your WebView
  3. in componentDidMount: this.webView.injectJavaScript('main()')
@Dryymoon

This comment has been minimized.

Show comment
Hide comment
@Dryymoon

Dryymoon May 3, 2017

"Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined" - is normal usecase in DEV mode, but in PRODUCTION this check isnt running. Die to code https://github.com/facebook/react-native/blob/master/React/Views/RCTWebView.m#L283

Dryymoon commented May 3, 2017

"Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined" - is normal usecase in DEV mode, but in PRODUCTION this check isnt running. Die to code https://github.com/facebook/react-native/blob/master/React/Views/RCTWebView.m#L283

@Dryymoon

This comment has been minimized.

Show comment
Hide comment
@Dryymoon

Dryymoon May 3, 2017

Maybe solve problem, but check for native code in RN: https://github.com/facebook/react-native/blob/master/React/Views/RCTWebView.m#L285
is Wrong, because

String(window.postMessage) === 'function () { [native code] }'

and

String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage') === 'function postMessage() { [native code] }'

And it not equal

But
Try THIS code:

function awaitPostMessage() {
  var isReactNativePostMessageReady = !!window.originalPostMessage;
  var queue = [];
  var currentPostMessageFn = function store(message) {
    if (queue.length > 100) queue.shift();
    queue.push(message);
  };
  if (!isReactNativePostMessageReady) {
    var originalPostMessage = window.postMessage;
    Object.defineProperty(window, 'postMessage', {
      configurable: true,
      enumerable: true,
      get: function () {
        return currentPostMessageFn;
      },
      set: function (fn) {
        currentPostMessageFn = fn;
        isReactNativePostMessageReady = true;
        setTimeout(sendQueue, 0);
      }
    });
    window.postMessage.toString = function () {
      return String(originalPostMessage);
    };
  }

  function sendQueue() {
    while (queue.length > 0) window.postMessage(queue.shift());
  }
}

Usage:

awaitPostMessage(); // Call this only once in your Web Code.

/* After you can call window.postMessage many times, and as soon as RN Message Bridge is ready - messages delivered */
window.postMessage('Once :) ');
window.postMessage('Twice :) ');

Notice: In my code i limit queue for 100 message, fell free to increase this, or remove limit at all.

Dryymoon commented May 3, 2017

Maybe solve problem, but check for native code in RN: https://github.com/facebook/react-native/blob/master/React/Views/RCTWebView.m#L285
is Wrong, because

String(window.postMessage) === 'function () { [native code] }'

and

String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage') === 'function postMessage() { [native code] }'

And it not equal

But
Try THIS code:

function awaitPostMessage() {
  var isReactNativePostMessageReady = !!window.originalPostMessage;
  var queue = [];
  var currentPostMessageFn = function store(message) {
    if (queue.length > 100) queue.shift();
    queue.push(message);
  };
  if (!isReactNativePostMessageReady) {
    var originalPostMessage = window.postMessage;
    Object.defineProperty(window, 'postMessage', {
      configurable: true,
      enumerable: true,
      get: function () {
        return currentPostMessageFn;
      },
      set: function (fn) {
        currentPostMessageFn = fn;
        isReactNativePostMessageReady = true;
        setTimeout(sendQueue, 0);
      }
    });
    window.postMessage.toString = function () {
      return String(originalPostMessage);
    };
  }

  function sendQueue() {
    while (queue.length > 0) window.postMessage(queue.shift());
  }
}

Usage:

awaitPostMessage(); // Call this only once in your Web Code.

/* After you can call window.postMessage many times, and as soon as RN Message Bridge is ready - messages delivered */
window.postMessage('Once :) ');
window.postMessage('Twice :) ');

Notice: In my code i limit queue for 100 message, fell free to increase this, or remove limit at all.

@Noitidart

This comment has been minimized.

Show comment
Hide comment
@Noitidart

Noitidart May 28, 2017

The solution @andreibarabas tested on Android works great for me on iOS.

How come we are going to so many extremes and not just using the postMessage.length method as @andreibarabas did? This one is so simple.

I load my webview with this:

<script>
    function whenRNPostMessageReady(cb) {
        if (postMessage.length === 1) cb();
        else setTimeout(function() { whenRNPostMessageReady(cb) }, 100);
    }
</script>

And usage of it:

<script>
    function whenRNPostMessageReady(cb) {
        if (postMessage.length === 1) cb();
        else setTimeout(function() { whenRNPostMessageReady(cb) }, 100);
    }


    whenRNPostMessageReady(function() {
        postMessage('hi react native!!!');
    });
</script>

Noitidart commented May 28, 2017

The solution @andreibarabas tested on Android works great for me on iOS.

How come we are going to so many extremes and not just using the postMessage.length method as @andreibarabas did? This one is so simple.

I load my webview with this:

<script>
    function whenRNPostMessageReady(cb) {
        if (postMessage.length === 1) cb();
        else setTimeout(function() { whenRNPostMessageReady(cb) }, 100);
    }
</script>

And usage of it:

<script>
    function whenRNPostMessageReady(cb) {
        if (postMessage.length === 1) cb();
        else setTimeout(function() { whenRNPostMessageReady(cb) }, 100);
    }


    whenRNPostMessageReady(function() {
        postMessage('hi react native!!!');
    });
</script>

@beiming beiming closed this Jun 6, 2017

@farazs

This comment has been minimized.

Show comment
Hide comment
@farazs

farazs Jun 6, 2017

Contributor

Why was this closed? It hasn't been resolved and the current implementation is flawed requiring complex workarounds.

Contributor

farazs commented Jun 6, 2017

Why was this closed? It hasn't been resolved and the current implementation is flawed requiring complex workarounds.

@stale stale bot removed the Stale label Oct 26, 2017

@jjzazuet

This comment has been minimized.

Show comment
Hide comment
@jjzazuet

jjzazuet Nov 18, 2017

I can confirm that the solution from @andreibarabas works for me in RN 0.50 under iOS and Android.

To be explicit:

import React, {Component} from 'react';
import {View, WebView} from 'react-native';

export default class WebViewTest extends Component {

  constructor(props) {
    super(props);
    this.state = {
      bridgeJs: `
        (function ready() {
          function whenRNPostMessageReady(cb) {
            if (postMessage.length === 1) cb();
            else setTimeout(function() { whenRNPostMessageReady(cb) }, 100);
          }
          whenRNPostMessageReady(function() {
            postMessage('hi react native!!!');
          });
        })();`
    };
  }

  onMessage(m) {
    alert(m.nativeEvent.data);
  }

  render() {
    return (
      <View style={{height: 0, opacity: 0}}>
        <WebView ref={(wv) => { this.webView = wv; }}
          injectedJavaScript={this.state.bridgeJs}
          onMessage={m => this.onMessage(m)} javaScriptEnabled />
      </View>
    );
  }
}

Thanks!

jjzazuet commented Nov 18, 2017

I can confirm that the solution from @andreibarabas works for me in RN 0.50 under iOS and Android.

To be explicit:

import React, {Component} from 'react';
import {View, WebView} from 'react-native';

export default class WebViewTest extends Component {

  constructor(props) {
    super(props);
    this.state = {
      bridgeJs: `
        (function ready() {
          function whenRNPostMessageReady(cb) {
            if (postMessage.length === 1) cb();
            else setTimeout(function() { whenRNPostMessageReady(cb) }, 100);
          }
          whenRNPostMessageReady(function() {
            postMessage('hi react native!!!');
          });
        })();`
    };
  }

  onMessage(m) {
    alert(m.nativeEvent.data);
  }

  render() {
    return (
      <View style={{height: 0, opacity: 0}}>
        <WebView ref={(wv) => { this.webView = wv; }}
          injectedJavaScript={this.state.bridgeJs}
          onMessage={m => this.onMessage(m)} javaScriptEnabled />
      </View>
    );
  }
}

Thanks!

@Noitidart

This comment has been minimized.

Show comment
Hide comment
@Noitidart

Noitidart Nov 18, 2017

Just an update to verify the code I got from above - #11594 (comment) - has been working perfectly for me on both, iOS and Android, since I started using it, in Jun 2017:

function init() {
    // ready to talk via postMessage
}

function whenRNPostMessageReady(cb) {
    if (postMessage.length === 1) cb();
    else setTimeout(function() { whenRNPostMessageReady(cb) }, 1000);
}

window.addEventListener('DOMContentLoaded', function() {
    whenRNPostMessageReady(init);
}, false);

Thanks @andreibarabas!!

Noitidart commented Nov 18, 2017

Just an update to verify the code I got from above - #11594 (comment) - has been working perfectly for me on both, iOS and Android, since I started using it, in Jun 2017:

function init() {
    // ready to talk via postMessage
}

function whenRNPostMessageReady(cb) {
    if (postMessage.length === 1) cb();
    else setTimeout(function() { whenRNPostMessageReady(cb) }, 1000);
}

window.addEventListener('DOMContentLoaded', function() {
    whenRNPostMessageReady(init);
}, false);

Thanks @andreibarabas!!

@jjzazuet

This comment has been minimized.

Show comment
Hide comment
@jjzazuet

jjzazuet Nov 19, 2017

Also, just to reiterate.

Placing comments inside the injectedJavaScript property of WebView breaks bridge communication on Android.

So don't do that I guess :P.

jjzazuet commented Nov 19, 2017

Also, just to reiterate.

Placing comments inside the injectedJavaScript property of WebView breaks bridge communication on Android.

So don't do that I guess :P.

@Noitidart

This comment has been minimized.

Show comment
Hide comment
@Noitidart

Noitidart Nov 19, 2017

I verify @jjzazuet comment - comments work on iOS but not on Android - thanks for that

Noitidart commented Nov 19, 2017

I verify @jjzazuet comment - comments work on iOS but not on Android - thanks for that

@nirpeled

This comment has been minimized.

Show comment
Hide comment
@nirpeled

nirpeled Nov 27, 2017

Can anyone please explain me why is it okay to do these "hacks" instead of just fixing RN's WebView so it won't override the global postMessage in a way that it's breaking the API? What am I missing here? Thanks!

nirpeled commented Nov 27, 2017

Can anyone please explain me why is it okay to do these "hacks" instead of just fixing RN's WebView so it won't override the global postMessage in a way that it's breaking the API? What am I missing here? Thanks!

@andrew09

This comment has been minimized.

Show comment
Hide comment
@andrew09

andrew09 Nov 30, 2017

I used @Dryymoon solution and it has worked really well. Experiencing some issues in ios9 but I am working on supporting that now. I will post an update with an updated solution.

andrew09 commented Nov 30, 2017

I used @Dryymoon solution and it has worked really well. Experiencing some issues in ios9 but I am working on supporting that now. I will post an update with an updated solution.

@vvavepacket

This comment has been minimized.

Show comment
Hide comment
@vvavepacket

vvavepacket Jan 3, 2018

guys, do you have any idea why a webview post message back and forth in a release build is super slow yet fast in a debug build?

vvavepacket commented Jan 3, 2018

guys, do you have any idea why a webview post message back and forth in a release build is super slow yet fast in a debug build?

@tranduykhanh

This comment has been minimized.

Show comment
Hide comment
@tranduykhanh

tranduykhanh Jan 15, 2018

@Dryymoon
This method still works. Thank a lot.
But there a lot of tricky here 😢

tranduykhanh commented Jan 15, 2018

@Dryymoon
This method still works. Thank a lot.
But there a lot of tricky here 😢

@mattbachman31

This comment has been minimized.

Show comment
Hide comment
@mattbachman31

mattbachman31 Jan 19, 2018

@jqn We also ran into that issue on our app, and the fix that is in RN 0.53-rc here fixes issues on page load, but not on form submit. I filed a separate bug for this issue here if anyone else is dealing with that issue

mattbachman31 commented Jan 19, 2018

@jqn We also ran into that issue on our app, and the fix that is in RN 0.53-rc here fixes issues on page load, but not on form submit. I filed a separate bug for this issue here if anyone else is dealing with that issue

@willhlaw

This comment has been minimized.

Show comment
Hide comment
@willhlaw

willhlaw Jan 19, 2018

I second @vvavepacket question. Why does webview post message seem much slower in production than in a debug build (at least in expo comparing development mode (~0.5s) vs production mode (>5s)?

willhlaw commented Jan 19, 2018

I second @vvavepacket question. Why does webview post message seem much slower in production than in a debug build (at least in expo comparing development mode (~0.5s) vs production mode (>5s)?

@Thomsos

This comment has been minimized.

Show comment
Hide comment
@Thomsos

Thomsos Mar 22, 2018

@willhlaw @vvavepacket I am experience the same issue in production build. Any work around or update on this?

I am using react-native 0.54.2 and react-native-wkwebview-reborn 1.16.0

Thomsos commented Mar 22, 2018

@willhlaw @vvavepacket I am experience the same issue in production build. Any work around or update on this?

I am using react-native 0.54.2 and react-native-wkwebview-reborn 1.16.0

@YusufHussein

This comment has been minimized.

Show comment
Hide comment
@YusufHussein

YusufHussein May 21, 2018

apparently injected javascript code doesn’t work in android unless compressed (you can use this online tool http://javascriptcompressor.com/ )

YusufHussein commented May 21, 2018

apparently injected javascript code doesn’t work in android unless compressed (you can use this online tool http://javascriptcompressor.com/ )

@Noitidart

This comment has been minimized.

Show comment
Hide comment
@Noitidart

Noitidart May 21, 2018

@YusufHussein it doesn't have to be compressed. You just cannot use any comments.

Noitidart commented May 21, 2018

@YusufHussein it doesn't have to be compressed. You just cannot use any comments.

@YusufHussein

This comment has been minimized.

Show comment
Hide comment
@YusufHussein

YusufHussein May 22, 2018

@Noitidart well I tried it without comments but it didn't work untill I compressed the code
I am using react native 0.54.2 and Android simulator 6.0 Marshmallow

YusufHussein commented May 22, 2018

@Noitidart well I tried it without comments but it didn't work untill I compressed the code
I am using react native 0.54.2 and Android simulator 6.0 Marshmallow

@stale

This comment has been minimized.

Show comment
Hide comment
@stale

stale bot Aug 20, 2018

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as "For Discussion" or "Good first issue" and I will leave it open. Thank you for your contributions.

stale bot commented Aug 20, 2018

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as "For Discussion" or "Good first issue" and I will leave it open. Thank you for your contributions.

@stale stale bot added the Stale label Aug 20, 2018

@kirillpisarev

This comment has been minimized.

Show comment
Hide comment
@kirillpisarev

kirillpisarev Aug 21, 2018

Still no luck posting a message on document ready within a WebView page without a dirty hack, suggested above. Is any hope that the problem will be solved without this mess, cuz it exists for about 2.5 years...

function RNPostMessage(message) {
      if (window.postMessage.length === 1) window.postMessage(message);
      else setTimeout(RNPostMessage.bind(null, message), 100);
}

kirillpisarev commented Aug 21, 2018

Still no luck posting a message on document ready within a WebView page without a dirty hack, suggested above. Is any hope that the problem will be solved without this mess, cuz it exists for about 2.5 years...

function RNPostMessage(message) {
      if (window.postMessage.length === 1) window.postMessage(message);
      else setTimeout(RNPostMessage.bind(null, message), 100);
}
@jamonholmgren

This comment has been minimized.

Show comment
Hide comment
@jamonholmgren

jamonholmgren Sep 9, 2018

Migrated issue to react-native-community/react-native-webview#5. This one can be closed.

jamonholmgren commented Sep 9, 2018

Migrated issue to react-native-community/react-native-webview#5. This one can be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment