简体   繁体   中英

External script with classes in React

I am trying to embed an external map service into my React app. Their recommendation to integrate the API into a regular HTML web page looks like this:

<script type="text/javascript" src="//map.search.ch/api/map.js"></script>
<script type="text/javascript">new SearchChMap({center:[123456,987654],y:"-1m",poigroups:"default"});</script>

I tried to apply this in my typescript React component as follows:

declare class SearchChMap {
  constructor(_: {})
}

// ...

<>
  <script type="text/javascript" src="//map.search.ch/api/map.js"></script>
  <script type="text/javascript">{new SearchChMap({ center: [123456, 987654], poigroups: "default" })}</script>
  {/* ... */}
</>

While this compiles, I receive the following runtime error: ReferenceError: SearchChMap is not defined .

What is the correct way of using legacy JavaScript classes from an externally hosted script in your React components?

Update:

I tried this answer to no avail, using the following code:

componentDidMount(): void {
  const script1: HTMLScriptElement = document.createElement("script");
  script1.src = "//map.search.ch/api/map.js";
  script1.async = true;
  document.body.appendChild(script1);

  const script2: HTMLScriptElement = document.createElement("script");
  script2.text = 'new SearchChMap({center:[123456,987654],y:"-1m",poigroups:"default"});';
  script2.async = true;
  document.body.appendChild(script2);
}

This results in the exact same error message, just a few seconds later, since the scripts are added dynamically.

Update 2:

Moving the object creation to onload as suggested by AWolf did the trick:

componentDidMount(): void {
  const scriptTag: HTMLScriptElement = document.createElement("script");
  scriptTag.src = "//map.search.ch/api/map.js";
  document.head.appendChild(scriptTag);

  scriptTag.onload = () => {
    new SearchChMap({ center: this.props.center, container: this.props.containerId });
  };
}

You could add the script tag to public/index.html and use it with window.SearchChMap or if you prefer to load it from your componentDidMount you can do it like in the snippet below (same code as in the following sandbox ).

It is using onload of the appended script tag to delay the object creation until the new script is loaded.

The useRef is used so you can use the created object in other locations of your function (not used in the example).

The useEffect in the code is required to ensure that the DOM is ready before adding the tag. Same behavior as in class-based components with componentDidMount lifecycle method. The return of the useEffect could be used to do cleanup eg return () => { /* remove script tag & dispose the window.SearchChMap */} (same as componentWillUnmount )

import React, { useEffect, useRef } from "react";

export default () => {
  const SearchMap = useRef(); // optional, but useful if the Map object is used after mounting
  useEffect(() => {
    const scriptTag = document.createElement("script");
    scriptTag.src = "//map.search.ch/api/map.js";
    document.body.appendChild(scriptTag);

    scriptTag.onload = () => {
      SearchMap.current = new window.SearchChMap({ center: "Zürich" });
    };
  }, [SearchMap]);

  return (
    <div
      id="mapcontainer"
      style={{ maxWidth: "500px", height: "400px", border: "2px inset #ccc" }}
    />
  );
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM