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?

5
  • How are you loading the scripts? <script> tags? Commented Mar 17, 2017 at 0:27
  • @bejado yes just for this component as we need it to be from a cdn Commented Mar 17, 2017 at 0:28
  • Put your application and React script tag below the CDN script tag, so the browser is forced to load the render function first. Commented Mar 17, 2017 at 0: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: google.com.au/…* Commented Mar 17, 2017 at 1:01
  • I've moved that to the index.html to be on safer side. Commented Mar 17, 2017 at 3:38

1 Answer 1

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>
    );
  }
}
Sign up to request clarification or add additional context in comments.

3 Comments

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 "appform.autoconvert.co.uk/assets/js/iframe/…" And function I want to use "initParentComms()"
can you please help me out from this problem
This doesn't work if the external script depends on some value from a redux store.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.