You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React集成Google Authentication:登录后登录按钮未自动隐藏问题

问题分析

核心问题是Google登录按钮的渲染未与user状态绑定,且登录成功后未主动处理按钮的隐藏逻辑,只有页面刷新时才会重新判断用户状态并决定是否渲染按钮:

  • 返回的JSX中,登录按钮DOM是无条件渲染的,不受user状态控制;
  • useEffect仅在组件挂载时执行一次,登录后user更新不会触发重新执行,无法处理按钮移除;
  • 依赖window.onload导致逻辑触发时机与React组件生命周期不匹配。
解决方案

1. 给登录按钮添加条件渲染

让登录按钮仅在user不存在时渲染,和应用UI形成互斥:

return (
  <div className="app">
    {user ? (
      // 已认证的应用UI
      <>...</>
    ) : (
      // 未认证时才显示登录按钮
      <div
        id="g_id_onload"
        data-client_id=""
        data-context="signin"
        data-ux_mode="popup"
      >
        <div
          className="g_id_signin"
          data-type="standard"
          data-shape="pill"
          data-theme="filled_blue"
          data-text="signin_with"
          data-size="large"
          data-logo_alignment="left"
        ></div>
      </div>
    )}
  </div>
);

2. 优化useEffect逻辑,移除window.onload

React组件挂载时useEffect已会执行,无需依赖window.onload,同时监听user状态变化,登录后销毁Google登录实例:

useEffect(() => {
  // 读取本地存储的用户信息
  const storedUser = JSON.parse(localStorage.getItem('user'));
  if (storedUser) {
    setUser(storedUser);
    return; // 已登录,跳过按钮初始化
  }

  // 未登录时初始化Google登录
  const signInOptions = {
    client_id: '你的Google Client ID',
  };

  google.accounts.id.initialize({
    client_id: signInOptions.client_id,
    callback: handleSignIn,
  });

  const buttonContainer = document.getElementById('g_id_onload');
  if (buttonContainer) {
    google.accounts.id.renderButton(buttonContainer, {
      theme: 'filled_blue',
      size: 'large',
      shape: 'pill',
      text: 'signin_with',
      logo_alignment: 'left',
      ux_mode: 'popup',
      context: 'signin',
    });
  }

  // 清理函数:组件卸载或user变化时销毁登录实例
  return () => {
    if (google.accounts.id) {
      google.accounts.id.cancel();
    }
  };
}, [user]); // 监听user状态变化

3. 完善登录回调函数,确保状态更新

登录成功后将用户信息存入本地存储并更新user状态,触发组件重新渲染以隐藏按钮:

const handleSignIn = (response) => {
  // 解析Google返回的凭证,构造用户对象
  const userData = {
    // 示例:根据实际需求提取用户信息
    id: response.credential,
    // 其他字段如姓名、邮箱等
  };
  
  localStorage.setItem('user', JSON.stringify(userData));
  setUser(userData); // 更新状态,触发组件重新渲染
};
修改后的完整代码示例
import { useEffect, useState, useRef } from 'react';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';

const App = () => {
  const [user, setUser] = useState(null);
  const [studentId, setStudentId] = useState('');
  const [unitCode, setUnitCode] = useState('');
  const [examYear, setExamYear] = useState('');
  const [loading, setLoading] = useState(false);
  const [rowData, setRowData] = useState([]);
  const gridRef = useRef(null);

  // 自定义列定义
  const columnDefs = [
    // 示例列,根据实际需求修改
    { field: 'studentId' },
    { field: 'unitCode' },
    { field: 'examYear' },
    { field: 'result' }
  ];

  const handleSignIn = (response) => {
    // 解析Google认证响应,构建用户数据
    const userData = {
      credential: response.credential,
      // 可通过Google的id_token解析更多用户信息
    };
    localStorage.setItem('user', JSON.stringify(userData));
    setUser(userData);
  };

  const handleStudentIdChange = (e) => setStudentId(e.target.value);
  const handleUnitCodeChange = (e) => setUnitCode(e.target.value);
  const handleExamYearChange = (e) => setExamYear(e.target.value);

  const handleSearchClick = () => {
    // 搜索逻辑实现
    setLoading(true);
    // 模拟异步请求
    setTimeout(() => {
      setRowData([
        { studentId, unitCode, examYear, result: 'Pass' }
      ]);
      setLoading(false);
    }, 1000);
  };

  const handleClearClick = () => {
    setStudentId('');
    setUnitCode('');
    setExamYear('');
    setRowData([]);
  };

  useEffect(() => {
    const storedUser = JSON.parse(localStorage.getItem('user'));
    if (storedUser) {
      setUser(storedUser);
      return;
    }

    const signInOptions = {
      client_id: '你的Google Client ID',
    };

    google.accounts.id.initialize({
      client_id: signInOptions.client_id,
      callback: handleSignIn,
    });

    const buttonContainer = document.getElementById('g_id_onload');
    if (buttonContainer) {
      google.accounts.id.renderButton(buttonContainer, {
        theme: 'filled_blue',
        size: 'large',
        shape: 'pill',
        text: 'signin_with',
        logo_alignment: 'left',
        ux_mode: 'popup',
        context: 'signin',
      });
    }

    return () => {
      if (google.accounts.id) {
        google.accounts.id.cancel();
      }
    };
  }, [user]);

  return (
    <div className="app">
      {user ? (
        <>
          <div className="header">
            <div className="logo-container">
              <img src="/logo.png" alt="Logo" className="logo" />
            </div>
            <div className="title">Special Consideration Attempt Lookup</div>
          </div>
          <div className="input-container">
            <div className="input-box">
              <label htmlFor="studentId">Student ID</label>
              <input type="text" id="studentId" value={studentId} onChange={handleStudentIdChange} />
              <span className="required-asterisk">*</span>
            </div>

            <div className="input-box">
              <label htmlFor="unitCode">Unit Code</label>
              <input type="text" id="unitCode" value={unitCode} onChange={handleUnitCodeChange} />
            </div>

            <div className="input-box">
              <label htmlFor="examYear">Exam Year</label>
              <input
                type="number"
                id="examYear"
                value={examYear}
                onChange={handleExamYearChange}
              />
              <span className="required-asterisk">*</span>
            </div>

            <button className="search-button" onClick={handleSearchClick}>
              Search
            </button>

            <button className="clear-button" onClick={handleClearClick}>
              Clear
            </button>
          </div>
          {loading && (
            <div className="loading-spinner-overlay">
              <div className="loading-spinner"></div>
            </div>
          )}
          <div
            className="ag-theme-alpine"
            style={{
              height: '400px',
              width: '100%',
              marginTop: '20px',
            }}
          >
            <AgGridReact
              columnDefs={columnDefs}
              rowData={rowData}
              onGridReady={(params) => {
                gridRef.current = params.api;
              }}
            />
          </div>
        </>
      ) : (
        <div
          id="g_id_onload"
          data-client_id="你的Google Client ID"
          data-context="signin"
          data-ux_mode="popup"
        >
          <div
            className="g_id_signin"
            data-type="standard"
            data-shape="pill"
            data-theme="filled_blue"
            data-text="signin_with"
            data-size="large"
            data-logo_alignment="left"
          ></div>
        </div>
      )}
    </div>
  );
};

export default App;

内容的提问来源于stack exchange,提问作者user3789276

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 09:34:57