Search code examples
reactjsgoogle-mapsgoogle-maps-api-3react-async

react-async-script implementation: wrapping a component


I am trying to wrap a react component that requires the google maps api, with a makeAsyncScriptLoader from react-async-script.

In the same file (if that makes a difference):

class SearchBar extends Component {
    /* Some stuff requiring the maps api */
}

const callbackName = "onloadcallback";
const URL = `https://maps.googleapis.com/maps/api/js?key=AIzaSyDSn_vNbNZzrcFxl8bV3MH1j0kuoLVsOCQ&callback=${callbackName}`;
const globalName = "foo";

export default makeAsyncScriptLoader(URL, {
  callbackName: callbackName,
  globalName: globalName
})(SearchBar);

My understanding, is that I should then be able to call the component from another file by importing SearchBarWrapper from the above file, and using it as <SearchBarWrapper />.

Basically, it doesn't work. The component tries to load without the script, and fails.


Solution

  • According to documentation the following properties could be specified to ensure JavaScript resource is finished loading:

    asyncScriptOnLoad: function : called after script finishes loading. using script.onload

    or

    callbackName: string : If the script needs to call a global function when finished loading (for example: recaptcha/api.js?onload=callbackName). Please provide the callback name here and it will be autoregistered on window for you.

    For the case of asyncScriptOnLoad event the following HOC could be introduced to ensure a wrapped component is rendered once JavaScript resource is loaded:

    function withScript(url, WrappedComponent) {
    
      const LoadingElement = props =>  {
          return <div>Loading...</div>;
      }
    
      class Wrapper extends React.PureComponent {
        constructor(props) {
          super(props);
          this.state = {
            scriptLoaded: false
          };
        }
        render() {
          const AsyncScriptLoader = makeAsyncScriptLoader(url)(LoadingElement);
          if(this.state.scriptLoaded){
             return <WrappedComponent/>
          } 
          return (
            <AsyncScriptLoader
              asyncScriptOnLoad={() => {
                this.setState({scriptLoaded: true});
              }}
            />
          );
        }
      }
      return Wrapper;
    }
    

    Now Google Maps component could be rendered like this:

    const GoogleMapsUrl = `https://maps.googleapis.com/maps/api/js?libraries=places&key=AIzaSyDurZQBXjtSzKeieXwtFeGe-jhZu-HEGQU`;
    const App = withScript(GoogleMapsUrl, Autocomplete);
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    

    Demo