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
相关产品推荐
相关产品推荐

