4

I am trying to implement this vanilla js example of google calendar api in a react js app. The vanilla JS example works just fine in my local machine. But am getting lot of troubles when implementing the same in react. Please check my code below:

class App extends React.Component{
  constructor(props) {
    super(props);
    var CLIENT_ID = '992549188018-3prg54pp18je3e3qhgcttgl11491c4dm.apps.googleusercontent.com';
    var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"];
    var SCOPES = "https://www.googleapis.com/auth/calendar";
    this.state = {
      showAuthButton: false,
      showSignOutButton: false
    };
    this.initClient = this.initClient.bind(this);
    this.updateSigninStatus = this.updateSigninStatus.bind(this);
  }
  handleAuthClick(){
    gapi.auth2.getAuthInstance().signIn();
  }
  handleSignoutClick(){
    gapi.auth2.getAuthInstance().signOut();
  }
  handleClientLoad() {
    gapi.load('client:auth2', this.initClient);
  }
  initClient(DISCOVERY_DOCS, CLIENT_ID, SCOPES) {
    gapi.client.init({
      discoveryDocs: DISCOVERY_DOCS,
      clientId: CLIENT_ID,
      scope: SCOPES
    }).then(function () {
      console.log(window.gapi);
      // Listen for sign-in state changes.
      window.gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);

      // Handle the initial sign-in state.
      updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
      authorizeButton.onclick = handleAuthClick;
      signoutButton.onclick = handleSignoutClick;
    });
  }
  updateSigninStatus(isSignedIn) {
    if (isSignedIn) {
      this.setState({
        showAuthButton: false,
        showSignOutButton: true
      })
      //listUpcomingEvents();
      //insertNewEvent();
    } else {
      this.setState({
        showAuthButton: true,
        showSignOutButton: false
      })
    }
  }
  componentDidMount(){
    this.handleClientLoad();
  }
  render(){
    let authButton = <button id="authorize-button" onClick={this.handleAuthClick.bind(this)}>Authorize</button>
    let signOutButton = <button id="signout-button" onClick={this.handleSignoutClick.bind(this)}>Sign Out</button>
    return(
      <div className="container">
        {this.state.showAuthButton ? authButton : null}
        {this.state.showSignOutButton ? signOutButton : null}
      </div>
    )
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

Am getting this error as of now:

Uncaught TypeError: Cannot read property 'isSignedIn' of null

Please guide me to move further...

6
  • You defined CLIENT_ID etc as variables in constructor but trying to access it as this.CLIENT_ID Commented Jul 22, 2017 at 16:07
  • @marzelin I have updated the code now, still getting the same error! Commented Jul 22, 2017 at 16:19
  • Either move config vars outside the component so that is can be reached by initClient method, or assign config data to this object and access it that way. Commented Jul 22, 2017 at 16:22
  • still getting the same error Commented Jul 22, 2017 at 16:37
  • It's hard to tell what's going on without having access to the whole app. Could you upload your code to github so that I could clone it and reproduce the error? Commented Jul 22, 2017 at 16:41

3 Answers 3

5

Finally adding my solution which worked to help future comers :)

To get the list of events, authorization is not required. So removed the unnecessary code from google's quickstart example.

componentDidMount = () => {
  this.getEvents();
}

getEvents(){
  let that = this;
  function start() {
    gapi.client.init({
      'apiKey': GOOGLE_API_KEY
    }).then(function() {
      return gapi.client.request({
        'path': `https://www.googleapis.com/calendar/v3/calendars/${CALENDAR_ID}/events`,
      })
    }).then( (response) => {
      let events = response.result.items
      that.setState({
        events
      }, ()=>{
        console.log(that.state.events);
      })
    }, function(reason) {
      console.log(reason);
    });
  }
  gapi.load('client', start)
}

You can find the full code and functional demo here.

Sign up to request clarification or add additional context in comments.

Comments

3

I'm using a HOC to load the google API:

import React from "react";
import { API_KEY } from "./constants";
import LoadingIndicator from "./common/LoadingIndicator";

export default function withGoogleApps(WrappedComponent) {
  class ComponentWithGoogleAPI extends React.Component {
    state = { gapiReady: false };

    componentDidMount() {
      this.loadGoogleAPI();
    }

    loadGoogleAPI() {
      const script = document.createElement("script");
      script.src = "https://apis.google.com/js/client.js";

      script.onload = () => {
        window.gapi.load("client", () => {
          window.gapi.client.setApiKey(API_KEY);
          window.gapi.client.load("calendar", "v3", () => {
            this.setState({ gapiReady: true });
          });
        });
      };

      document.body.appendChild(script);
    }

    render() {
      const { gapiReady } = this.state;
      if (gapiReady) return <WrappedComponent />;
      return <LoadingIndicator />;
    }
  }
  return ComponentWithGoogleAPI;
}

The you call it like this:

import withGoogleApps from "./withGoogleApps";

const AppConGoogle = withGoogleApps(App);

Then you can call gogle API's like calendar, for example:

const event = this.buildEventoGoogle(values);

const request = window.gapi.client.calendar.events.insert({
      calendarId: "primary",
      resource: event
});

Comments

2

You moved gapi config variables outside of the component, but didn't remove them from initClient parameters so they couldn't be accessed. Here's proper code:

var CLIENT_ID = '992549188018-3prg54pp18je3e3qhgcttgl11491c4dm.apps.googleusercontent.com';
var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"];
var SCOPES = "https://www.googleapis.com/auth/calendar";

class App extends React.Component{
  constructor(props) {
    super(props);
    this.state = {
      showAuthButton: false,
      showSignOutButton: false
    };
    this.initClient = this.initClient.bind(this);
    this.updateSigninStatus = this.updateSigninStatus.bind(this);
  }
  handleAuthClick(){
    gapi.auth2.getAuthInstance().signIn();
  }
  handleSignoutClick(){
    gapi.auth2.getAuthInstance().signOut();
  }
  handleClientLoad() {
    gapi.load('client:auth2', this.initClient);
  }
  initClient(/****here you've had parameters that made config vars unaccessible*****/) {
    gapi.client.init({
      discoveryDocs: DISCOVERY_DOCS,
      clientId: CLIENT_ID,
      scope: SCOPES
    }).then(function () {
      console.log(window.gapi);
      // Listen for sign-in state changes.

      // ************* to access instance method you have to use `this.updateSigninStatus`  
  window.gapi.auth2.getAuthInstance().isSignedIn.listen(this.updateSigninStatus);

      // Handle the initial sign-in state.
      updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());

      // **************this code is unnecessary and causes errors***** 
      // authorizeButton.onclick = handleAuthClick;
      // signoutButton.onclick = handleSignoutClick;
    });
  }
  updateSigninStatus(isSignedIn) {
    if (isSignedIn) {
      this.setState({
        showAuthButton: false,
        showSignOutButton: true
      })
      //listUpcomingEvents();
      //insertNewEvent();
    } else {
      this.setState({
        showAuthButton: true,
        showSignOutButton: false
      })
    }
  }
  componentDidMount(){
    this.handleClientLoad();
  }
  render(){
    let authButton = <button id="authorize-button" onClick={this.handleAuthClick.bind(this)}>Authorize</button>
    let signOutButton = <button id="signout-button" onClick={this.handleSignoutClick.bind(this)}>Sign Out</button>
    return(
      <div className="container">
        {this.state.showAuthButton ? authButton : null}
        {this.state.showSignOutButton ? signOutButton : null}
      </div>
    )
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

1 Comment

I couldn't get where am making mistake, thanks for your help and time anyway :)

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.