简体   繁体   English

HTML脚本在React组件加载后加载

[英]HTML script is loading AFTER react components

My index.html 我的index.html

<!DOCTYPE html>
<html lang="en">
    <head>

        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <meta name="google-signin-client_id" content= "my_client_id.apps.googleusercontent.com">

        <meta name="google-signin-scope" content="profile email">
        <script src="https://apis.google.com/js/client:platform.js?onload=start" async defer></script>
        <script>
            function start() {
                console.log('script running')
                gapi.load('auth2', function() {
                    auth2 = gapi.auth2.init({
                        client_id: 'my_client_id.apps.googleusercontent.com',
                        scope: 'profile email'
                    });
                });
            }
        </script>
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>


  </body>
</html>

In the start() function I print to the console to see when it's running. start()函数中,我打印到控制台以查看其何时运行。

When I load the page, every so often start() will load after the react components. 当我加载页面时,每隔很长时间start()都会 react组件之后加载。

Login.js Login.js

    componentDidMount() {
        console.log(gapi.auth2.getAuthInstance())
    }

In the debugger you can see that the script is loading after the component: 在调试器中,您可以看到脚本正在组件之后加载:

在此处输入图片说明

If I refresh the page a few times, it works fine. 如果我刷新页面几次,则可以正常工作。 But sometimes it works, sometimes it doesn't. 但是有时它起作用,有时却不起作用。

Why? 为什么?

I think the best way to load scripts in react is with a container component. 我认为在React中加载脚本的最好方法是使用容器组件。 It's a pretty simple component and it allows to you write the logic for importing the script in a component rather than your index.html. 这是一个非常简单的组件,它允许您编写将脚本导入组件的逻辑,而不是index.html。 You are also going to want to make sure you don't include the script more than once by calling loadScript after a check in componentDidMount. 您还将要通过在检入componentDidMount之后调用loadScript来确保不多次包含脚本。

This is adapted from: https://www.fullstackreact.com/articles/how-to-write-a-google-maps-react-component/ 改编自: https : //www.fullstackreact.com/articles/how-to-write-a-google-maps-react-component/

Something like this. 这样的事情。 . .

  componentDidMount() {
    if (!window.google) {
      this.loadMapScript();
    }
    else if (!window.google.maps) {
      this.loadMapScript();
    }
    else {
      this.setState({ apiLoaded: true })
    }
  }

  loadMapScript() {
    // Load the google maps api script when the component is mounted.

    loadScript('https://maps.googleapis.com/maps/api/js?key=YOUR_KEY')
      .then((script) => {
        // Grab the script object in case it is ever needed.
        this.mapScript = script;
        this.setState({ apiLoaded: true });
      })
      .catch((err: Error) => {
        console.error(err.message);
      });
  }

  render() {
    return (
      <div className={this.props.className}>
        {this.state.apiLoaded ? (
          <Map
            zoom={10}
            position={{ lat: 43.0795, lng: -75.7507 }}
          />
        ) : (
          <LoadingCircle />
        )}
      </div>
    );
  }

and then in a separate file: 然后在一个单独的文件中:

const loadScript = (url) => new Promise((resolve, reject) => {
  let ready = false;
  if (!document) {
    reject(new Error('Document was not defined'));
  }
  const tag = document.getElementsByTagName('script')[0];
  const script = document.createElement('script');

  script.type = 'text/javascript';
  script.src = url;
  script.async = true;
  script.onreadystatechange = () => {
    if (!ready && (!this.readyState || this.readyState === 'complete')) {
      ready = true;
      resolve(script);
    }
  };
  script.onload = script.onreadystatechange;

  script.onerror = (msg) => {
    console.log(msg);
    reject(new Error('Error loading script.'));
  };

  script.onabort = (msg) => {
    console.log(msg);
    reject(new Error('Script loading aboirted.'));
  };

  if (tag.parentNode != null) {
    tag.parentNode.insertBefore(script, tag);
  }
});


export default loadScript;

I know it's a lot but when I was first doing this it was so relieving when I found out there was a (fairly) simple way of including any script in any react component. 我知道很多东西,但是当我第一次这样做的时候,当我发现有一种(相当)简单的方法将任何脚本包含在任何react组件中时,这真让我感到宽慰。

Edit: Some of this I just copy pasted but if you aren't using create-react-app you will probably have to replace some of the ES6 syntax. 编辑:其中一些我只是复制粘贴,但是如果您不使用create-react-app,则可能必须替换某些ES6语法。

My suggestions: 我的建议:

Change your google API script tag to be this, where you remove async and defer 将您的google API脚本标记更改为this,在其中删除asyncdefer

<script src="https://apis.google.com/js/client:platform.js"></script>

Get rid of your start function, which will now run the console.log fine and dandy, but the second bit of code will cause the same issue as it will also be running asynchronously. 摆脱掉启动函数,该函数现在可以正常运行console.log ,但第二部分代码将引起相同的问题,因为它也将异步运行。

Modify your react code, so that the componentWillMount calls the contents of that function instead: 修改您的反应代码,以便componentWillMount代替地调用该函数的内容:

componentWillMount() {
  gapi.load('auth2', () => {
    auth2 = gapi.auth2.init({
      client_id: 'urhaxorid.apps.googleusercontent.com',
      scope: 'profile email',
      onLoad: () => {
        this.setState({ mapLoaded: true });
      }
    });
  });
}

componentDidMount() {
  if (this.state.mapLoaded) {
    console.log(gapi.auth2.getAuthInstance());
  }
}

Please bear in mind that I don't know what the onLoad is for google apis, and I am not 100% sure how to best do the setState stuff, but it may be a starting block. 请记住,我不知道Google api的onLoad是什么,并且我不确定100%如何最好地完成setState ,但这可能是一个起步。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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