2

I want to execute main() after making sure loading all libraries.

useEffect(() => { 
    for (const id in urls) {
      let tag = document.createElement('script');
      tag.async = false;
      tag.src = urls[id];
      let body = document.getElementsByTagName('body')[0];
      body.appendChild(tag);
    }
    main()
},[]); 
Ori Drori
  • 145,770
  • 24
  • 170
  • 162
  • 1
    Writing a component that loads scripts like this just doesn't seem like a good idea in general. I would suggest loading those scripts before even calling `ReactDOM.render()` – Patrick Roberts Dec 13 '19 at 16:49
  • Does this answer your question? [Call javascript function after script is loaded](https://stackoverflow.com/questions/14644558/call-javascript-function-after-script-is-loaded) – Agney Dec 13 '19 at 16:50
  • `after making sure loading all libraries` Please show how do you load these libraries, it's hard to answer you question with very few detail – Vencovsky Dec 13 '19 at 17:16
  • I have a list of urls in ```const urls = ["https:...js", "http...js"];``` – Masaki Minamide Dec 14 '19 at 00:30

1 Answers1

0

Create a loadScript() function that returns a promise. The promise is resolved via the load event handler:

const loadScript = url => new Promise(resolve => {
  const tag = document.createElement('script');
  tag.async = false;
  tag.src = url;
  const body = document.body;
  body.appendChild(tag);
  tag.addEventListener('load', resolve, {
    once: true
  });
});

Now you can map the urls to promises by loading the script, and use Promise.all() to wait for the loading of the entire batch:

useEffect(() => {
  Promise.all(Object.values(urls).map(loadScript))
    .then(main);
}, [urls]);

You can create a custom useScripts hook to encapsulate this functionality:

const { useEffect, useState } = React;

const loadScript = (url, target) => new Promise(resolve => {
  const tag = document.createElement('script');
  tag.async = false;
  tag.src = url;
  target.appendChild(tag);
  tag.addEventListener('load', resolve, {
    once: true
  });
});

const useScripts = (urls, cb = () => {}, deps) => {
  useEffect(() => {
    const body = document.body;
  
    Promise.all(urls.map(url => loadScript(url, body)))
      .then(cb);
  }, deps);
}

const Demo = ({ urls }) => {
  const [loading, setLoading] = useState(true);
  
  useScripts(Object.values(urls), () => setLoading(false), [urls, setLoading]);
  
  return loading ? 'Loading' : 'done';
};

const urls = { lodash: 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js', moment: 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.js' };

ReactDOM.render(
  <Demo urls={urls} />,
  root
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>
Ori Drori
  • 145,770
  • 24
  • 170
  • 162