Search code examples
react-nativewebviewfetch

Correct way to fetch JSON with React Native's WebView


It seems WebView plays a similar role of web workers for react-native.

I am trying to offload heavy data fetch to a WebView but it seems very painful and could not find a fetch example with WebView.

So I have below JavaScript code with which I am trying to call the API.

When using the exact same code from a regular react-native class, the JSON response is perfectly fetched and parsed.

But when the same JavaScript code is injected into a WebView with injectJavaScript method, the Content-Type value causes the problem. (When I remove it, I see from the backend that the call is made but I can not get the JSON data at the frontend - WebView side-.)

It seems it is related to cors even though the API backend allows all cross-origin requests.

As a result, my questions are:

  1. What is a proper way to use fetch with WebView in react-native?
  2. Why the behaviour is different between react-native and WebView?
var l_headers = {
   Accept: 'application/json',
   'Content-Type': 'application/json'
};
var l_init = {
    method: "POST",
    headers: l_headers,
    body: {}
};
fetch("http://172.20.10.12:8000/test", l_init).then(function (response) {
    return response.json();
}).then(function (responseJson) {
    alert('API called: ' + JSON.stringify(responseJson));
});

PS: One final handicap, please note that I am also using EXPO and not able to eject because of its benefits. That is why I can not use react-native-community's react-native-webview as of today. (It seems in future this will be adapted for EXPO).


Solution

  • Update
    Following code snippet, I am fetching JSON through POST fetch and once fetched the response gets shown inside alert. Here is a working Snack Expo Link.

      injectjs() {
        let jsCode = `const bodyData = JSON.stringify({
          title: 'foo',
          body: 'bar',
          userId: 1
        });
        fetch('https://jsonplaceholder.typicode.com/posts', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: bodyData,
        }).then(response => response.text()).then(valueText => {
          alert(JSON.stringify(valueText));
        });`;
        return jsCode;
      }
    
      render() {
        return (
          <View style={styles.container}>
           <WebView
              ref={webview => { this.webview = webview; }}
              source={{
                uri: "https://www.google.com"
                }}
                injectedJavaScript={this.injectjs()}
                javaScriptEnabled = {true}
                style={styles.webview}
              />
          </View>
        );
      }
    

    Old Answer
    I usually use postMessage in ReactNative WebView when I need to communicate between HTML and react native code.

    • HTML code to send message to react native
        postMessage(JSON.stringify(yourJson), '*');
    
    • Receive message from react native
    
        document.addEventListener('message',(event) => {
        eval(event, data)
        }, false)
    
    
    • React Native WebView
        <WebView
               ref={webview => { this.webview = webview; }}
               source={anySource}
               injectedJavaScript={script}
               javaScriptEnabled = {true}
               onMessage={this.onMessage}
               onNavigationStateChange = {this.handleNavigation}
             />
    
    • Receive message
    
        onMessage = (e) => {
         let { data } = e.nativeEvent; // data you will receive from html
        }
    
    
    • To post message
        this.webview.postMessage('YourMessage')
    

    If postMessage doesn't work in expo you can use onShouldStartLoadWithRequest / onNavigationStateChange method instead.

        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
          }