Front End to Back End: Communicating With a React Native WebView

In my latest blog post, I talked about how we decided to make an existing web application work offline by running it in a WebView inside of React Native. This allowed us to reuse our front-end code. But what about our back-end code? One of the reasons we chose React Native is because it can run JavaScript. Since our application was written in TypeScript, we figured we could use our existing code in a React Native/offline context.

We eventually got our back-end logic to run in React Native, but we needed a way for our front end to communicate with the back end.

In this post, I am going to show you how we set up a communication layer between the code running in our WebView and our React Native application.

Setup

To get started, we are going to use the project shown in my last blog post. You can either complete that tutorial here, or use this GitHub repository as a starting point.

WebView to React Native Communication

Any code that is running in a WebView in React Native has an API that allows it to communicate with the code running in the React Native application: window.ReactNativeWebView.postMessage

Whenever this is called from within the WebView, it triggers the onMessage callback on the WebView.

Let’s make a couple of changes to our current project to demonstrate this. In assets/www/index.js, add the following line:

window.ReactNativeWebView.postMessage("Hello React Native!");

Let’s also change our WebView to handle that request:

<WebView
  style={{ flex: 1, marginBottom: 20 }}
  source={{ uri: this.state.url }}
  onMessage={event => {
    const { data } = event.nativeEvent;
    console.log(data);
  }}
/>

If you run react-native run-ios with a debugger attached, you should see “Hello React Native” in your logs.

React Native to WebView

So we’ve proven that we can communicate from our WebView to React Native. But what about the other way around? This can be done by injecting code into our WebView and using the postMessage web API.

In order to inject code into our WebView’s code, we first need a reference to our WebView:

<WebView
  ref={webView => (this.webView = webView)}
  style={{ flex: 1, marginBottom: 20 }}
  source={{ uri: this.state.url }}
  onMessage={event => {
    const { data } = event.nativeEvent;
    console.log(data);
  }}
/>

 

Now we have a reference to our WebView, so we can inject our `postMessage` code into it like so:


<WebView
  ref={webView => (this.webView = webView)}
  style={{ flex: 1, marginBottom: 20 }}
  source={{ uri: this.state.url }}
  onMessage={event => {
    const { data } = event.nativeEvent;
    const clientResponseCode = `
      window.postMessage(${JSON.stringify(data)}, "*");
      true;
    `;
    if (this.webView) {
      this.webView.injectJavaScript(clientResponseCode);
    }
  }}
/

Now that we are sending messages to our WebView, we should also be responding to them. So let’s add the following code to assets/www/index.js:


window.addEventListener("message", message => {
  document.getElementById("newStuff").innerHTML += `</br>${message.data}`;
});

Now if you re-run the application, you should see “Hello React Native” In your WebView.

And that’s it! You should have a WebView that is able to communicate with the application that is running it.

If you would like to see this code in its final form, clone this repo, and check out the `communication` branch.