Home > Enterprise >  ReactJS call function async and wait for the response before continuing (Login Google Workspace then
ReactJS call function async and wait for the response before continuing (Login Google Workspace then

Time:10-18

I am building a ReactJS application that uses Google Workspace and a Python backend for logging in users.

The flow is as following

  • useEffect() loads a Google sign in button
  • On frontend the users click "Sign in with Google"
  • The frontend calls the function handleCallbackResponse() which prints the Google token.
  • The handleCallbackResponse() should send the Google token to the Python backend. The function needs to wait until a response from backend is given. The call to backend happens in loginBackend.
  • My problem is that the variable const backendToken is set to "[object Promise]". However I need it to be set to the response from the backend.

I created a drawing of the flow:

enter image description here

index.html

I added Google client script

<script src="https://accounts.google.com/gsi/client" async defer></script>

App.js

import React from 'react';
import { BrowserRouter, Routes ,Route } from 'react-router-dom';
import './App.css';
import Login from './pages/Login/Login';
import useToken from './useToken';


import Layout from "./pages/Layout";
import Home from "./pages/Home";
import About from "./pages/About";
import NoPage from "./pages/NoPage";


function App() {

  const { token, setToken } = useToken();

  if(!token) {
    return <Login setToken={setToken} />
  }

  return (
    <div className="wrapper">
      <h1>Application</h1>
      <BrowserRouter>
          <Routes>
              <Route path="/" element={<Layout />}>
                  <Route index element={<Home />} />
                  <Route path="about" element={<About />} />
                  <Route path="*" element={<NoPage />} />
              </Route>
          </Routes>
      </BrowserRouter>
    </div>
  );
}

export default App;

useToken.js

import { useState } from 'react';

export default function useToken() {
  const getToken = () => {
    const tokenString = localStorage.getItem('token');
    const userToken = JSON.parse(tokenString);
    return userToken?.token
  };

  const [token, setToken] = useState(getToken());

  const saveToken = userToken => {
    localStorage.setItem('token', JSON.stringify(userToken));
    setToken(userToken.token);
    console.log(userToken.token)
  };

  return {
    setToken: saveToken,
    token
  }
}

pages/Login/Login.js

import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import './Login.css';
import jwt_decode from "jwt-decode"

const google = window.google;
export default function Login({ setToken }) {

  // Handle response from Google login
  function handleCallbackResponse(response){
    console.log("Google token: "   response.credential)
    var userObject = jwt_decode(response.credential)
    var userEmail = userObject.email;
    console.log("userEmail="   userEmail)
    document.getElementById("sign_in_form_google").hidden = true;

    // Call login on backend async  async e => {loginBackend(response.credential)};
    const backendToken = loginBackend(response.credential)
    console.log("Backend token: "   backendToken)
    setToken(backendToken);

  }

  // Login to backend
  async function loginBackend(credentials){
    console.log("Login.js :: loginBackend():: Logging in with "   credentials)
    var backendToken = fetch('https://localhost:5000/api/login', {
        method: 'POST',
        headers: {
          'Authorization': 'Bearer '   credentials

        }
      }).then(data => data.json())
    console.log("Login.js :: loginBackend() :: Response is "   backendToken)
    return backendToken
  }

  // Google Login 
  useEffect(() => {
    /* global google */
    google.accounts.id.initialize({
      client_id: "xxx-yyy.apps.googleusercontent.com",
      callback: handleCallbackResponse
    });

    google.accounts.id.renderButton(
      document.getElementById("sign_in_button_google"),
      { theme: "outline", size: "large" }
    )

    google.accounts.id.prompt();

  }, []);

  return(
    <div className="body_bg">
      <div className="wrapper">
          {/* User login header */}
          <header>
            <div className='header_left'>
              <p>My website</p>
            </div>
            <div className='header_right'>
              <img src="gfx/login/logo_100x49.png" alt="Logo" />
            </div>
          </header>

          {/* User login main */}
          <main>
            <h1>Please Log In</h1>

            {/* Login form */ }
            <div id="sign_in_form_google">
              <p>Use Google to login below.</p>
              <div id="sign_in_button_google"></div>
            </div>
          </main>

          {/* User login footer */}
          <footer>
            <p>&copy; 2022 Me</p>
          </footer>
      </div>
    </div>
  )
}

Login.propTypes = {
  setToken: PropTypes.func.isRequired
};

pages/Layout.js:

import { Outlet, Link } from "react-router-dom";

const Layout = () => {
  return (
    <>
      <nav>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
        </ul>
      </nav>

      <Outlet />
    </>
  )
};

export default Layout;

pages/Home.js:

import { getValue } from "@testing-library/user-event/dist/utils";
import { useContext, useEffect, useState, useMemo } from 'react';
import { UserContext } from "../useToken";

import jwt_decode from "jwt-decode"

export function Home() {

  return (
    <div>
      <h1>Home</h1>

    </div>
    );
}
  
export default Home;

paegs/About.js:

import { useContext } from "react";

export function About() {

  return (
    <div>
      <h1>About</h1>

      

    </div>
    );
}
  
export default About;

pages/NoPage.js:

const NoPage = () => {
    return <h1>404</h1>;
  };
    
  export default NoPage;

Console output:

Login.js:11 Google token: eyxxx
Login.js:14 [email protected]
Login.js:26 Login.js :: loginBackend():: Logging in with eyxxx
Login.js:35 Login.js :: loginBackend() :: Response is [object Promise]
Login.js:19 Backend token: [object Promise]
useToken.js:15 undefined

CodePudding user response:

Make handleCallbackResponse async and await loginBackend:

    async function handleCallbackResponse(response){
        console.log("Google token: "   response.credential)
        var userObject = jwt_decode(response.credential)
        var userEmail = userObject.email;
        console.log("userEmail="   userEmail)
        document.getElementById("sign_in_form_google").hidden = true;
    
        // Call login on backend async  async e => {loginBackend(response.credential)};
        const backendToken = await loginBackend(response.credential)
        console.log("Backend token: "   backendToken)
        setToken(backendToken);
    }
  • Related