React集成AWS Cognito登录后组件不更新及Context报错问题
问题:React + AWS Cognito 登录后无需刷新动态切换组件
我正在学习React,开发一个使用AWS Cognito的应用。有一套视频教程很有帮助,但没讲登录后如何实现无刷新的组件切换/状态更新。
当前遇到的问题:
- Settings组件仅对已认证用户显示,但登录后必须刷新页面才会出现,希望实现无需刷新就能显示
- Settings组件中出现
AccountContext is not defined错误 - 想要点击登录按钮时能动态显示/登录相关元素和用户专属组件
现有代码片段
App.js
export default () => { return ( <Account> <Status /> <Signup /> <Login /> <ForgotPassword /> <Settings /> </Account> ); };
正确的Settings组件代码(注:原粘贴内容存在重复App.js的错误,以下为实际使用的Settings组件)
import React, { useState, useEffect, useContext } from "react"; import { AccountContext } from "./Accounts"; import ChangePassword from "./ChangePassword"; import ChangeEmail from "./ChangeEmail"; // eslint-disable-next-line import/no-anonymous-default-export export default () => { const [loggedIn, setLoggedIn] = useState(false); const { getSession } = useContext(AccountContext); useEffect(() => { getSession().then(() => { setLoggedIn(true); }).catch((err) => console.log("Catch", err) ) }, [getSession]); return ( <div> {loggedIn && ( <> <h1>Settings</h1> <ChangePassword /> <ChangeEmail /> </> )} </div> ); };
Account组件
import React, { createContext } from "react"; import { CognitoUser, AuthenticationDetails } from "amazon-cognito-identity-js"; import Pool from "../UserPool"; const AccountContext = createContext(); const Account = props => { const getSession = async () => await new Promise((resolve, reject) => { const user = Pool.getCurrentUser(); if (user) { user.getSession(async (err, session) => { if (err) { reject(); } else { const attributes = await new Promise((resolve, reject) => { user.getUserAttributes((err, attributes) => { if (err) { reject(err); } else { const results = {}; for (let attribute of attributes) { const { Name, Value } = attribute; results[Name] = Value; } resolve(results); } }); }); resolve({ user, ...session, ...attributes }); } }); } else { reject(); } }); const authenticate = async (Username, Password) => await new Promise((resolve, reject) => { Username = "<username>"; Password = "<password>"; const user = new CognitoUser({ Username, Pool }); const authDetails = new AuthenticationDetails({ Username, Password }); user.authenticateUser(authDetails, { onSuccess: data => { console.log("onSuccess:", data); resolve(data); }, onFailure: err => { console.error("onFailure:", err); reject(err); }, newPasswordRequired: data => { console.log("newPasswordRequired:", data); resolve(data); } }); }); const logout = () => { const user = Pool.getCurrentUser(); if (user) { user.signOut(); } }; return ( <AccountContext.Provider value={{ authenticate, getSession, logout }} > {props.children} </AccountContext.Provider> ); }; export { Account, AccountContext };
解决方案
1. 修复AccountContext is not defined错误
先排查导入问题:
- 确认Account组件所在文件名为
Accounts.js(因为Settings组件中导入路径是./Accounts),如果实际文件名是Account.js,则需要把导入路径改成./Account - 检查文件名和路径的大小写是否匹配(React对文件路径大小写敏感)
2. 实现登录后无刷新更新组件状态
核心思路是在Account组件中维护全局的登录会话状态,登录/登出时更新状态,让Context自动通知所有子组件重新渲染:
修改Account组件,添加状态管理
import React, { createContext, useState, useEffect } from "react"; import { CognitoUser, AuthenticationDetails } from "amazon-cognito-identity-js"; import Pool from "../UserPool"; const AccountContext = createContext(); const Account = props => { const [session, setSession] = useState(null); const [loading, setLoading] = useState(true); // 初始化时自动获取会话状态 useEffect(() => { getSession().then(newSession => { setSession(newSession); }).catch(() => { setSession(null); }).finally(() => { setLoading(false); }); }, []); const getSession = async () => await new Promise((resolve, reject) => { const user = Pool.getCurrentUser(); if (user) { user.getSession(async (err, session) => { if (err) { reject(); } else { const attributes = await new Promise((resolve, reject) => { user.getUserAttributes((err, attributes) => { if (err) { reject(err); } else { const results = {}; for (let attribute of attributes) { const { Name, Value } = attribute; results[Name] = Value; } resolve(results); } }); }); resolve({ user, ...session, ...attributes }); } }); } else { reject(); } }); const authenticate = async (Username, Password) => await new Promise((resolve, reject) => { // 移除硬编码的用户名密码,使用传入的参数 const user = new CognitoUser({ Username, Pool }); const authDetails = new AuthenticationDetails({ Username, Password }); user.authenticateUser(authDetails, { onSuccess: async (data) => { console.log("onSuccess:", data); // 登录成功后获取完整会话并更新状态 const newSession = await getSession(); setSession(newSession); resolve(data); }, onFailure: err => { console.error("onFailure:", err); reject(err); }, newPasswordRequired: data => { console.log("newPasswordRequired:", data); resolve(data); } }); }); const logout = () => { const user = Pool.getCurrentUser(); if (user) { user.signOut(); setSession(null); // 登出后同步更新状态 } }; return ( <AccountContext.Provider value={{ authenticate, getSession, logout, session, // 将会话状态传入Context loading }} > {!loading && props.children} </AccountContext.Provider> ); }; export { Account, AccountContext };
修改Settings组件,直接读取Context中的会话状态
import React, { useContext } from "react"; import { AccountContext } from "./Accounts"; import ChangePassword from "./ChangePassword"; import ChangeEmail from "./ChangeEmail"; export default () => { const { session } = useContext(AccountContext); return ( <div> {session && ( <> <h1>Settings</h1> <ChangePassword /> <ChangeEmail /> </> )} </div> ); };
修改App.js,根据会话状态动态切换组件
import React, { useContext } from "react"; import { AccountContext } from "./Accounts"; import Status from "./Status"; import Signup from "./Signup"; import Login from "./Login"; import ForgotPassword from "./ForgotPassword"; import Settings from "./Settings"; export default () => { const { session } = useContext(AccountContext); return ( <Account> <Status /> {/* 未登录时显示登录/注册组件 */} {!session && ( <> <Signup /> <Login /> <ForgotPassword /> </> )} {/* Settings组件会根据session状态自动显示/隐藏 */} <Settings /> </Account> ); };
3. 关键说明
- Account组件维护全局的
session状态,登录/登出时更新状态,Context会自动触发所有子组件的重新渲染,无需手动刷新 - 移除Settings组件中手动检查会话的逻辑,直接读取Context中的
session状态,避免状态不同步的问题 - 登录成功后主动获取完整会话信息并更新状态,确保组件能实时感知登录状态变化
内容的提问来源于stack exchange,提问作者Nick Wright
相关产品推荐
相关产品推荐

