Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to embed a React/Redux app in another React/Redux app without running into lifecycle issues?

App A is a self-contained, single-page React/Redux application that is meant to be pluggable into other applications (not necessarily written in React/Redux). I'm trying to embed app A in another single-page React/Redux app B as follows:

  • In app B, create a component, ComponentA, whose route is the same as that used by app A, and whose render() method generates a DIV tag to host app A.

  • In ComponentA's componentWillMount() method, load the previously built js bundles for app A, by appending the corresponding <script> tags at the end of the document body, which causes app A to render on the page.

This seems to work fine, however, when I navigate away from ComponentA and come back, I see the following error appear in the Chrome Dev Tools console as many times as I have accessed ComponentA: "Uncaught (in promise) TypeError: Cannot read property 'replaceChild' of null".

When I examine the "React" tab of the Dev Tools, I can see app A's component hierarchy appear multiple times, however, only the last one is actually attached to the physical DOM.

My guess is that when I navigate to other sections of app B (using app B's links), app A isn't aware of the route changes, so it is never unmounted, which causes some unintended behaviour and subsequent errors.

I'm new to the technology, and I'm wondering if it is even possible to embed a React/Redux app in another React/Redux app. If it is, then how could I change my approach to make it work properly?

I haven't included any code snippets for now, because the code is pretty standard, basic React/Redux code, and it's the approach itself that I question.

like image 453
pikkabird Avatar asked Jan 05 '18 00:01

pikkabird


1 Answers

I was able to resolve the issue by modifying the code in app A's entry point and changing how the app is built.

Before the fix:

  • App A was built as a self-contained, self-rendering application.
  • App B's ComponentA was adding app A's bundle script tags in its componentWillMount() method.
  • App A's entry JSX was executing ReactDOM.render() immediately, and there was no way to unmount it from within app B.

After the fix:

  • App A is built as a library (specified as such in the Webpack config).
  • App A's entry JSX defines the library interface, exposing custom new(), render(), and unmount() methods.
  • App A's bundle script tags are hardcoded in app B's index.html.
  • App B's ComponentA:
    • in componentWillMount(): instantiates app A.
    • in componentDidMount(): calls app A's render().
    • in componentWillUnmount(): calls app A's unmount(), which calls ReactDOM.unmountComponentAtNode().

I'm now seeing only one instance of app A's component hierarchy in the "React" tab, and no more errors appear in the console.

For anyone interested, the fix was inspired by the following article that I eventually came across: https://codeburst.io/building-react-widget-libraries-using-webpack-e0a140c16ce4

like image 61
pikkabird Avatar answered Nov 12 '22 13:11

pikkabird