2
votes

I am using react-router. I have embedded the same external widget in two react components. The widget loads fine for the first time or when I refresh the page. The problem is when I change pages, the widget won't load anymore. Please look at my code:

var { Router, Route, IndexRoute, Link, browserHistory } = ReactRouter

var MainLayout = React.createClass({
  render: function() {
    return (
      <div className="app">
        <header className="primary-header"></header>
        <aside className="primary-aside">
          <ul>
            <li><Link to="/">Home</Link></li>
            <li><Link to="/page1">Page1</Link></li>
            <li><Link to="/page2">Page2</Link></li>
          </ul>
        </aside>
        <main>
          {this.props.children}
        </main>
      </div>
      )
  }
})

var Home = React.createClass({
  render: function() {
    return (<h1>Home Page</h1>)
  }
})

var SearchLayout = React.createClass({
  render: function() {
    return (
      <div className="search">
        <header className="search-header"></header>
        <div className="results">
          {this.props.children}
        </div>
        <div className="search-footer pagination"></div>
      </div>
      )
  }
})

var PageOne = React.createClass({
  componentDidMount: function() {
    const plumxScript = document.createElement("script");
    plumxScript.src = "//d39af2mgp1pqhg.cloudfront.net/widget-popup.js";
    plumxScript.async = true;
    document.body.appendChild(plumxScript);
 },

  render: function() {
    return (
        <div
            href="https://plu.mx/plum/a/?doi=10.1371/journal.pone.0056506"
            className="plumx-plum-print-popup"
            data-hide-when-empty="true"
            data-popup="left"
            data-size="medium"
            target="_blank">
        </div>
      )
  }
})

var PageTwo = React.createClass({
  componentDidMount: function() {
    const plumxScript = document.createElement("script");
    plumxScript.src = "//d39af2mgp1pqhg.cloudfront.net/widget-popup.js";
    plumxScript.async = true;
    document.body.appendChild(plumxScript);
  },

  render: function() {
    return (
        <div
            href="https://plu.mx/plum/a/?doi=10.1371/journal.pone.0056506"
            className="plumx-plum-print-popup"
            data-hide-when-empty="true"
            data-popup="left"
            data-size="medium"
            target="_blank">
        </div>
      )
  }
})

ReactDOM.render((
  <Router>
    <Route path="/" component={MainLayout}>
      <IndexRoute component={Home} />
      <Route component={SearchLayout}>
        <Route path="page1" component={PageOne} />
        <Route path="page2" component={PageTwo} />
      </Route> 
    </Route>
  </Router>
), document.getElementById('root'))

Click on Page1 and the widget loads, click on Page2 and in theory the widget should reload but it doesn't. If I click again on Page1 the widget won't load.

Full code here: https://codepen.io/vh_ruelas/pen/MbgdOm

1

1 Answers

1
votes

The issue seems to be related to the script you're loading, not react-router. Apparently, it doesn't work as expected when you load it multiple times, which doesn't seem like a clean solution anyways.

If you load the script only once, you're left with the problem that the placeholder nodes that are rendered later won't be transformed automatically, since the script only initializes them when it is loaded (source). Luckily, the script adds a (apparently undocumented) function you can call to solve this: window.__plumX.widgets.popup.wireUp().

But there's another problem with the script in combination with React: It heavily modifies the DOM rendered by React, which causes all kinds of problems. There's a lot information about this, e.g., here. Apart from re-implementing the widget in React yourself, the best way seems to be to have React render a container <div> and then insert the widget node manually.

Here's a class that takes care of these two problems and seems to work the way you want:

var PlumXPopup = React.createClass({
  componentDidMount() {
    this.appendNode();
    this.appendScript();
  },

  appendNode: function() {
    const plumxNode = document.createElement("div");
    plumxNode.className = "plumx-plum-print-popup";
    plumxNode.setAttribute('href', 'https://plu.mx/plum/a/?doi=10.1371/journal.pone.0056506');
    plumxNode.setAttribute('data-hide-when-empty', 'true');
    plumxNode.setAttribute('data-popup', 'left');
    plumxNode.setAttribute('data-size', 'medium');
    plumxNode.setAttribute('target', '_blank');
    this.divNode.appendChild(plumxNode);
  },

  appendScript: function() {
    if (document.getElementById('plumx-script')) {
      if (window.__plumX) {
        window.__plumX.widgets.popup.wireUp();
      }
      return;
    }
    const plumxScript = document.createElement("script");
    plumxScript.id = 'plumx-script';
    plumxScript.src = "//d39af2mgp1pqhg.cloudfront.net/widget-popup.js";
    plumxScript.async = true;
    document.body.appendChild(plumxScript);
  },

  render: function() {
    var _this = this;
    return <div ref={function(ref) { _this.divNode = ref }} />
  }
});

You can use it instead of the placeholder <div> in PageOne and PageTwo, and also get rid of the script loading there. You might also want to add props to use when creating the placeholder node.