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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 19:20:47