Delaying react component load until helmet head script loads

Viewed 4174

I have a react component that relies on the google maps js api. This is imported in the head of the App.js page with a script tag. When I try to load the page containing this component, it fails, claiming that the script is not loaded. If, however, I first open a different page (which causes the script to load, but does not include this component), and then navigate to the page containing the component, it works.

This suggests to me that the issue is that the script has not yet loaded when the component loads. I thought, though, that the page loading should block while it gets the script. I'm not really sure how to fix this.

App.js:

class App extends Component {
  render() {
    return (
      <Router>
        <div>
          <Helmet>
            <script
              type="text/javascript"
              src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDSn_vNbNZzrcFxl8bV3MH1j0kuoLVsOCQ&libraries=places"
            />
          </Helmet>

          <div className="pageContent">
                <Route exact path="/foo" component={FooScreen} />
                <Route exact path="/bar" component={BarScreen} /
          </div>
        </div>
      </Router>
    );
  }
}
export default App;

In the above, FooScreen contains the component, and BarScreen doesn't. If I open FooScreen directly, it fails, if I open BarScreen, and then navigate to FooScreen, it works.

1 Answers

Usually event listeners should be attached to script element instance, e.g. promise-based script loader.

This case is specific to Google Maps or other scripts that have asynchronous initialization routine and accept a callback, so it's redundant to listen to load event:

  state = { mapLoaded: false };

  componentDidMount() {
    window.onGoogleMap = () => this.setState({ mapLoaded: true });
  }

  componentWillUnmount() {
    delete window.onGoogleMap;
  }

  render() {
    return <>
      <Helmet>
        <script
          type="text/javascript"
          src="https://maps.googleapis.com/maps/api/js?key=...&libraries=places&callback=onGoogleMap"
        />
      </Helmet>

      {!this.state.mapLoaded ? (
        'Loading...'
      ) : (
        <Router>...</Router>
      )}
    </>;
  }

Since global variable is in use, this requires no more than one instance of App to exist at the same time, including tests.

Related