15

I want to load the script from a CDN and then execute a function exposed by that script in React:

componentWillMount() {
    console.log('componentWillMount is called');
    const script = document.createElement('script');
    script.src = 'https://foo.azurewebsites.net/foo.js';
    document.body.appendChild(script);
}


componentDidMount() {
    console.log('componentDidMount is called');
    window.foo.render({
        formId: '77fd8848-791a-4f13-9c82-d24f9290edd7',
    }, '#container');
}


render() {
    console.log('render is called');
    return (
        <div id="container"></div>
    );
}

The script sometimes takes time to load (generally first time) and when componentDidMount() is called "foo" is not available and I get an error like this:

TypeError: Cannot read property 'render' of undefined

How can I assure that componentDidMount() is called once the script is loaded successfully?

Joshua Kleveter
  • 1,769
  • 17
  • 23
Abhijeet Ahuja
  • 5,596
  • 5
  • 42
  • 50
  • How are you loading the scripts? ` – bejado Mar 17 '17 at 00:27
  • @bejado yes just for this component as we need it to be from a cdn – Abhijeet Ahuja Mar 17 '17 at 00:28
  • Put your application and React script tag below the CDN script tag, so the browser is forced to load the `render` function first. – bejado Mar 17 '17 at 00:29
  • Looks like there is an `onload` functionality when appending scripts to the document body, so you could have some say a boolean in the component's state to keep track of load status, and in the `onload` function set the state's 'loaded' boolean to true. Then in your render function check the state value. Here's a google search: https://www.google.com.au/search?q=javascript+document+append+callback&oq=javascript+document+append+callback&aqs=chrome..69i57.5495j0j1&sourceid=chrome&ie=UTF-8#q=javascript+document+append+script+callback&* – Jayce444 Mar 17 '17 at 01:01
  • I've moved that to the index.html to be on safer side. – Abhijeet Ahuja Mar 17 '17 at 03:38

1 Answers1

26

I don't think it's a good idea to load scripts in componentWillMount() or componentDidMount(), according to React Component Specs and Lifecycle.

The code below may help you.

function new_script(src) {
  return new Promise(function(resolve, reject){
    var script = document.createElement('script');
    script.src = src;
    script.addEventListener('load', function () {
      resolve();
    });
    script.addEventListener('error', function (e) {
      reject(e);
    });
    document.body.appendChild(script);
  })
};
// Promise Interface can ensure load the script only once.
var my_script = new_script('http://example.com/aaa.js');

class App extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      status: 'start'
    };
  }

  do_load = () => {
    var self = this;
    my_script.then(function() {
      self.setState({'status': 'done'});
    }).catch(function() {
      self.setState({'status': 'error'});
    })
  }

  render() {
    var self = this;
    if (self.state.status === 'start') {
      self.state.status = 'loading';
      setTimeout(function () {
        self.do_load()
      }, 0);
    }

    return (
      <div>{self.state.status}   {self.state.status === 'done' && 'here you can use the script loaded'}</div>
    );
  }
}
Sam Lindstrom
  • 59
  • 1
  • 12
Y.Peng
  • 400
  • 3
  • 5
  • 1
    Hi, I have same problem but I want to call a function which refer to third party script. When I am using ReactJS's build command its giving me error that function is not defined. My script is "https://appform.autoconvert.co.uk/assets/js/iframe/quickcarfinanceabtest/parent-comms.js" And function I want to use "initParentComms()" – Jagdeesh Kumar Feb 24 '20 at 10:57
  • can you please help me out from this problem – Jagdeesh Kumar Feb 24 '20 at 10:58
  • This doesn't work if the external script depends on some value from a redux store. – mrudult May 17 '21 at 15:07