React-Redux: Google OAuth User ID ๐Ÿ‘ฎโ€โ™€๏ธ

Intro

In order to differentiate between our users inside of our Twitch Clone, we are going to use their Google ID Number. We can get this number with GAPI using the following process.

Getting the User ID

Once a user is logged in we can retrieve their user ID using GAPI with the following request.

gapi.auth2.getAuthInstance().currentUser.get().getId()

user ID in console

Of course that is just in the console, we need to request this number and track it with state inside of our application.

Saving UserId to State with Redux

We have already setup redux to track the โ€œSigned Inโ€ status of the user, now we need to add an additional piece of state.

Currently in our component, after the component loads we start listening for changes to the authentication status of the user. If the authentication status changes, we trigger the onAuthChange function. Depending on whether isSignedIn is true or false we will trigger one of two actions, sign in, or sign out.

If we are signing in, we also now want to retrieve the userID. So we can simply add that request as an argument in the signIn() function, and then manage that in the action later.

components/GoogleAuth.js
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { connect } from "react-redux";

import { signIn, signOut } from "../actions";

class GoogleAuth extends Component {

  componentDidMount() {
    window.gapi.load("auth2", () => {
      window.gapi.auth2
        .init({
          client_id: process.env.REACT_APP_GOOGLE_OAUTH2_CLIENT_ID,
          scope: "email",
        })
        .then(() => {
          // create auth variable
          this.auth = window.gapi.auth2.getAuthInstance();
          // update state so that component will re-render
          this.onAuthChange(this.auth.isSignedIn.get());
          // listen for changes to authentication status
          this.auth.isSignedIn.listen(this.onAuthChange);
        });
    });
  }

  // triggered when authentication status changes
  onAuthChange = (isSignedIn) => {
    if (isSignedIn) {
      this.props.signIn(this.auth.currentUser.get().getId());    } else {
      this.props.signOut();
    }
  };

  // manually trigger GAPI auth change
  onSignInClick = () => {
    this.auth.signIn();
  };

  onSignOutClick = () => {
    this.auth.signOut();
  };

  // helper function
  renderAuthButton() {
    if (this.props.isSignedIn === null) {
      return null;
    } else if (this.props.isSignedIn) {
      return (
        <button onClick={this.onSignOutClick} className="ui red google button">
          <i className="google icon" />
          Sign Out
        </button>
      );
    } else {
      return (
        <button onClick={this.onSignInClick} className="ui red google button">
          <i className="google icon" />
          Sign In
        </button>
      );
    }
  }

  render() {
    return (
      <Link to="/" className="item">
        <div>{this.renderAuthButton()}</div>
      </Link>
    );
  }
}

const mapStateToProps = (state) => {
  return { isSignedIn: state.auth.isSignedIn };
}

export default connect(mapStateToProps, { signIn, signOut })(GoogleAuth);

then in our action we can receive that user ID as an argument, and add it as a payload in our action.

actions/index.js
import { SIGN_IN, SIGN_OUT } from './types'

export const signIn = (userId) => {  return {
    type: SIGN_IN,
    payload: userId  };
};

export const signOut = () => {
  return {
    type: SIGN_OUT,
  };
};

Which then gets sent to the reducer, where the user id is received off the payload and is added as a piece of state in the store.

reducers/authReducer.js
import { SIGN_IN, SIGN_OUT } from '../actions/types'

const INITIAL_STATE = {
    isSignedIn: null,
    userId: null};

export default (state = INITIAL_STATE, action) => {
    switch (action.type) {
        case SIGN_IN:
            return {...state, isSignedIn: true, userId: action.payload };        case SIGN_OUT:
            return {...state, isSignedIn: false };
        default:
            return state;
    }
};

Lastly, we want to make sure that when the user signs out we set the user ID back to null. So we can simply add that to the SIGN_OUT reducer.

reducers/authReducer.js
import { SIGN_IN, SIGN_OUT } from '../actions/types'

const INITIAL_STATE = {
    isSignedIn: null,
    userId: null
};

export default (state = INITIAL_STATE, action) => {
    switch (action.type) {
        case SIGN_IN:
            return {...state, isSignedIn: true, userId: action.payload };
        case SIGN_OUT:
            return {...state, isSignedIn: false, userId: null };        default:
            return state;
    }
};

GitHub Repo

Ncoughlin: React-Streams-Client