Search code examples
javascriptreactjswidgetembed

embed react widget in other react project


I build react widget by this tutorial https://tekinico.medium.com/build-a-react-embeddable-widget-c46b7f7999d8 I squashed all the files by bundler tool called parcel.

after I have the two files (js and css), I'm trying to host this widget in other react project so I opened react project and in index.html I added the js and css files

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="theme-color" content="#000000" />
  <meta name="description" content="Web site created using create-react-app" />
  <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />

  <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

  <title>React App</title>
</head>

<body>
  <noscript>You need to enable JavaScript to run this app.</noscript>
  <div id="root"></div>

  <link href="https://tekinico.com/demo/react-widget/index.css" rel="stylesheet" />
  <script src="https://tekinico.com/demo/react-widget/index.js"></script>
  <!-- <link href="https://firebasestorage.googleapis.com/v0/b/fir-firestore-9702f.appspot.com/o/index.css?alt=media&token=62a7013c-185c-4f9b-ae56-fd2572c769ef" rel="stylesheet"/>
  <script  src="https://firebasestorage.googleapis.com/v0/b/fir-firestore-9702f.appspot.com/o/index.js?alt=media&token=55069135-ea09-480c-a297-354f009c497b"></script> -->

</html>

in app.js I added the div with the class "nicoraynaud-finance-widget" for the widget

 import './App.css';
    function App() {
      return (
        <div className="App">
        <div  data-symbol="GME" className="nicoraynaud-finance-widget" style={{'width': '250px', 'margin': 'auto', 'marginTop': '20px'}}></div>
        </div>
      );
    }
    
    export default App;

but in the browser I don't see nothing. it loaded the css and js files, I get the error of devtools, in network it shows it get loaded the script and css correctly enter image description here enter image description here enter image description here but I see blank window. enter image description here

that's the widget I'm trying to host enter image description here


Solution

  • Your widget is not showing in your react app because the widget script is loaded and run before your react app has been injected into the DOM. So your widget is trying to query for and inject html into the 'finance-widget' div, however, that div does not yet exist in the DOM (on the page).

    You need to split up the loading of the script and the initialization of the widget React app in order to ensure your main App component has been mounted in the page prior to the initialization of the widget.

    Go to your widget app, in index.js:

    You can change:

    // Inject our React App into each class
    widgetDivs.forEach(div => {
        ReactDOM.render(
          <React.StrictMode>
            <App symbol={div.dataset.symbol}/>
          </React.StrictMode>,
            div
        );
    });
    

    to:

    // wrap the react app injection in an init function
    window.initFinanceWidget = function() {
      // important to query for 'nicoraynaud-finance-widget' class when we are ready to initialize the widget
      const widgetDivs = document.querySelectorAll('.nicoraynaud-finance-widget');
      // Inject our React App into each class
      widgetDivs.forEach(div => {
        ReactDOM.render(
          <React.StrictMode>
            <App symbol={div.dataset.symbol}/>
          </React.StrictMode>,
            div
        );
      });
    }
    

    Then, back in your main project, init the widget in your App component (app.js) after the component has mounted, using a useEffect hook

    import { useEffect } from 'react';
    import './App.css';
    function App() {
      useEffect(() => {
        window.initFinanceWidget();
      }, [])
      return (
        <div className="App">
          <div  data-symbol="GME" className="nicoraynaud-finance-widget" style={{'width': '250px', 'margin': 'auto', 'marginTop': '20px'}}></div>
        </div>
      );
    }
        
    export default App;