inokawa / react-native-react-bridge

An easy way to integrate your React (or Preact/React Native Web) app into React Native app with WebView.
https://www.npmjs.com/package/react-native-react-bridge
MIT License
269 stars 21 forks source link

Trouble Loading Local Script in Expo App with react-native-react-bridge #132

Open accelerate-dm opened 1 year ago

accelerate-dm commented 1 year ago

We are currently facing an issue with loading a local script file in our Expo app when using the react-native-react-bridge library. The goal is to render a React component within the Expo app and enable communication between the native and web components using the emit method. Our main challenge lies in loading a script file stored locally. We've attempted various methods, including using a Promise with document.createElement('script'), as shown below:

function new_script(src) {
        return new Promise(function(resolve, reject){
          var script = document.createElement('script');
          script.src = src;
          script.type = 'text/javascript';
          script.addEventListener('load', function () {
            console.log('resolved');
            resolve();
          });
          script.addEventListener('error', function (e) {
            console.log(e);
            reject(e);
          });
          document.body.appendChild(script);
        })
      };
      var myScript = new_script("packages/app/features/stockfishScript/myAlert.js");
      myScript.then(res => {
        console.log(res);
      }).catch(e => {
        console.log(e)
      })

Additionally, we've explored using libraries like "react-script-tag" and "react-helmet" but have not been successful in loading the script. Interestingly, the same script loads as expected when used in a React web app without "react-native-react-bridge." Here's a simplified example of our React code that utilizes "react-native-react-bridge": // WebApp.js

import React, { useState, useEffect } from "react";
import {
  webViewRender,
  emit,
  useNativeMessage,
} from "react-native-react-bridge/lib/web";

const SampleReact = () => {
  alert("ON REACT CODE")
  const [data, setData] = useState("ON REACT CODE");
  // useNativeMessage hook receives message from React Native
  useNativeMessage((message) => {
    if (message.type === "success") {
      setData(message.data);
    }
  });

  useEffect(() => {

      function new_script(src) {
        return new Promise(function(resolve, reject){
          var script = document.createElement('script');
          script.src = src;
          script.type = 'text/javascript';
          script.addEventListener('load', function () {
            console.log('resolved');
            resolve();
          });
          script.addEventListener('error', function (e) {
            console.log(e);
            reject(e);
          });
          document.body.appendChild(script);
        })
      };
      var myScript = new_script("packages/app/features/stockfishScript/myAlert.js");
      myScript.then(res => {
        console.log(res);
      }).catch(e => {
        console.log(e)
      })

  }, []);

  return (
    <div>
      <p>React App new</p>
    </div>
  );
};

export default webViewRender
(<SampleReact />);

On the native side, we have a component like this:

import {
  Paragraph,
} from '@my/ui'
import { ScrollView, YStack, ListItem } from 'tamagui'
import HelloWorld from 'app/features/canvas/screen'
import { Stack } from 'expo-router'
import WebView from "react-native-webview";
import { useWebViewMessage } from "react-native-react-bridge";
import webApp from "app/features/MyAlert/SampleReact";
import { useRef } from 'react';
import { StatusBar } from 'react-native';

export default function Screen() {
  const webviewRef = useRef<any>();
  // alert('CANVAS')
  const myRef = useRef(null);
  const { ref, onMessage, emit } = useWebViewMessage((message) => {
    console.log(message.type, 'MESSAGE');
    alert(message);
    // if (message.type === "hello" && message.data === 123) {
    //   emit({ type: "success", data: "succeeded!" });
    // }
  });

  return (
    <>
      <Stack.Screen
        options={{
          title: 'Chessingly - Canvas',
        }}
      />
    <WebView
      ref={ref}
      source={{ html: webApp }}
      onMessage={event => onMessage(event)}
    />
    </>
  )
}

We'd greatly appreciate any insights or suggestions to resolve this issue and successfully load the local script in our Expo app when using "react-native-react-bridge." Thank you!

inokawa commented 1 year ago

All files resolved from webViewRender entry point will be bundled into one file and run in WebView.

In that process, all require() and import will be bundle by metro bundler but others like var myScript = new_script("packages/app/features/stockfishScript/myAlert.js"); will not. So I think you have to replace the path with actual code in the build process by yourself.

accelerate-dm commented 1 year ago

All files resolved from webViewRender entry point will be bundled into one file and run in WebView.

In that process, all require() and import will be bundle by metro bundler but others like var myScript = new_script("packages/app/features/stockfishScript/myAlert.js"); will not. So I think you have to replace the path with actual code in the build process by yourself.

Thank you for the quick response.

Are you suggesting we use the new_script function as is but instead of using a path we copy the whole code block in myAlert.js inside the double quotes? Or should we not use the new_script function at all and refer to our code in a different way? We're struggling to understand how we should reference the code block. This is our standalone setup that runs by itself in html/js that we are trying to run inside the react component using react-native-react-bridge: https://gist.github.com/zserge/6617cdbf328aac76913ff9ae85350c10