从第三方JS文件onload设置ReactJS状态

时间:2017-04-25 18:44:43

标签: javascript reactjs gapi

我基本上是尝试在React组件类的componentWillMount()函数内执行此操作。

    const script = document.createElement("script");

    script.src = "https://apis.google.com/js/client.js";
    script.async = true;
    script.onload = function() {
      gapi.client.setApiKey(API_KEY);
      gapi.client.load('youtube', 'v3');
      this.setState({ gapiReady: true }); 
      // ^ Doesn't work because `this` refers to the script rather than the component itself.
    };

    document.body.appendChild(script);

此处粘贴整个组件以确保完整性:

import React, { Component } from 'react';
import VideoPlayer from '../../components/VideoPlayer/VideoPlayer';
import VideoInfo from '../../components/VideoInfo/VideoInfo';
import SearchBox from '../../components/SearchBox/SearchBox';
import './App.css';

class App extends Component {
  constructor(props, context) {
    super(props, context);
    this.state = {};
  };

  componentWillMount() {
    const script = document.createElement("script");

    script.src = "https://apis.google.com/js/client.js";
    script.async = true;
    script.onload = function() {
      gapi.client.setApiKey(API_KEY);
      gapi.client.load('youtube', 'v3');
      this.setState({ gapiReady: true }); // Doesn't work because `this` refers to the script rather than the component
    };

    document.body.appendChild(script);
  };

  render() {
    if (this.state.gapiReady) {
      return (
        <div className="wrap">
          <div className="sidebar">
            <SearchBox />
            <VideoInfo videoId="vkRDOcma9Qk" />
          </div>
          <div className="content">
            <VideoPlayer />
          </div>
        </div>
      );
    } else {
      return (
        <div>Loading...</div>
      )
    }
  };
}

export default App;

我的目标是仅在脚本完全加载时在render()函数中呈现VideoInfo和VideoPlayer组件,否则这些组件上的代码将失败,因为它们依赖于已加载的脚本。

我是以错误的方式解决这个问题吗?

1 个答案:

答案 0 :(得分:3)

首先,你应该做这样的事情是componentDidMount,这样即使代码无法访问document对象,代码仍然可以运行。如果您不进行服务器端渲染,这并不重要,但它是React中的常见模式。

要解决this的问题,您可以使用箭头功能定义script.onload。使用箭头函数定义的函数不会创建新的上下文,因此this保持与定义函数的作用域相同。

TLDR将此componentWillMount方法替换为:

componentDidMount () {
  const script = document.createElement("script")

  script.src = "https://apis.google.com/js/client.js"
  script.async = true

  script.onload = () => {
    gapi.client.setApiKey(API_KEY)
    gapi.client.load('youtube', 'v3')
    this.setState({ gapiReady: true })
  }

  document.body.appendChild(script)
}