Skip to content

react native html postMessage can not reach to WebView #11594

Closed

Description

@peixin

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

Activity

  1. anttu commented on Jan 8, 2017

    @anttu

    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.

  2. farazs commented on Jan 11, 2017

    @farazs

    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?

  3. Dryymoon commented on Jan 24, 2017

    @Dryymoon

    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.

  4. sylvainbaronnet commented on Jan 27, 2017

    @sylvainbaronnet

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

  5. ministerofpanic commented on Jan 29, 2017

    @ministerofpanic

    @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.

  6. jqn commented on Jan 31, 2017

    @jqn

    @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");
    });
    
  7. gp3gp3gp3 commented on Feb 8, 2017

    @gp3gp3gp3

    @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.

  8. andreibarabas commented on Feb 21, 2017

    @andreibarabas

    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>
    
  9. farazs commented on Mar 1, 2017

    @farazs

    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.

  10. doxiaodong commented on Mar 15, 2017

    @doxiaodong

    @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();
    
  11. farazs commented on Apr 11, 2017

    @farazs

    @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.

  12. kyle-ilantzis commented on Apr 12, 2017

    @kyle-ilantzis

    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"

  13. chriscohoat commented on Apr 21, 2017

    @chriscohoat

    @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 .

  14. shenlq commented on Apr 27, 2017

    @shenlq

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

  15. kyle-ilantzis commented on Apr 27, 2017

    @kyle-ilantzis

    @shenlq if you disable proguard then it should work again

    In android/app/build.gradle

    def enableProguardInReleaseBuilds = false

  16. 27 remaining items

  17. Karasynth commented on Mar 22, 2018

    @Karasynth

    @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

  18. y-ghuzzi commented on May 21, 2018

    @y-ghuzzi

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

  19. Noitidart commented on May 21, 2018

    @Noitidart

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

  20. y-ghuzzi commented on May 22, 2018

    @y-ghuzzi

    @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

  21. stale commented on Aug 20, 2018

    @stale

    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.

  22. added
    StaleThere has been a lack of activity on this issue and it may be closed soon.
    on Aug 20, 2018
  23. kirillpisarev commented on Aug 21, 2018

    @kirillpisarev

    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);
    }
    
  24. removed
    StaleThere has been a lack of activity on this issue and it may be closed soon.
    on Aug 21, 2018
  25. jamonholmgren commented on Sep 9, 2018

    @jamonholmgren
    Collaborator

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

  26. vladikoff commented on Nov 6, 2018

    @vladikoff

    Quick note about this fix for Android, if you used postMessage.length === 1 fix for your Android app, Chrome 70 (and maybe 69?) now reports postMessage.length as 1. So your app might be broken.

    Use the [native code] fix or something like postMessage.toString().includes('__REACT_WEB_VIEW_BRIDGE')

  27. locked as resolved and limited conversation to collaborators on Nov 6, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions