1

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 });
  };
}
Pascal Kesseli
  • 1,620
  • 1
  • 21
  • 37
  • Does this answer your question? [How do i use external script that i add to react JS?](https://stackoverflow.com/questions/53396307/how-do-i-use-external-script-that-i-add-to-react-js) – glinda93 Jul 04 '20 at 18:08
  • Thanks for the reference. Please see my update in the question. – Pascal Kesseli Jul 04 '20 at 18:37
  • Adding the map object creation in the `onload` method of the script tag will help to fix the loading issue. The following [Codesandbox](https://codesandbox.io/s/searchapi-usage-with-external-script-g87dv) is working. – AWolf Jul 04 '20 at 18:49
  • 1
    Thanks @AWolf, this worked, as described in my update above. Feel free to turn your comment into an answer and I'll accept it. – Pascal Kesseli Jul 04 '20 at 19:26

1 Answers1

2

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 e.g. 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" }}
    />
  );
};
AWolf
  • 8,770
  • 5
  • 33
  • 39