属性更新后更新用户上下文 - AWS Amplify

Posted

技术标签:

【中文标题】属性更新后更新用户上下文 - AWS Amplify【英文标题】:Update User Context After Attribute Update - AWS Amplify 【发布时间】:2019-12-16 04:00:26 【问题描述】:

我有一个要求,即首次用户在登录后会看到一个他们需要同意的弹出窗口。我在 Cognito 中创建了一个自定义属性,该属性标记为“是”,直到用户单击同意按钮。所有这些逻辑都有效,除非您刷新页面时再次向用户显示弹出窗口,尽管同意并且在 Cognito 中更改了属性。

我正在使用带有 useContext 钩子的 React 上下文 API。我在 React 工具中注意到上下文没有更新,这可能是问题所在。

AuthContext.js

import React from 'react';

export const AuthContext = React.createContext();

export const AuthProvider = AuthContext.Provider;

App.js

import React from 'react';
import  withRouter  from 'react-router-dom';
import Header from './components/Header';
import Routes from './Routes';
import useAmplifyAuth from './libs/useAmplifyAuth';
import  AuthProvider  from './context/AuthContext';
import InitialLoginModal from './components/InitialLoginModal';

function App() 
  const 
    state:  user ,
    handleSignout
   = useAmplifyAuth();

  return (
    <>
      <AuthProvider value= user, handleSignout >
        <>
          <Header />
          <Routes />
          <InitialLoginModal />
        </>
      </AuthProvider>
    </>
  );


export default withRouter(App);

InitialLoginModal.js

import React,  useContext, useState, useEffect  from 'react';
import  Modal, Button, Image  from 'react-bootstrap';
import  Auth  from 'aws-amplify';
import imgLogo from '../img/logo.jpg';
import  AuthContext  from '../context/AuthContext';

const InitialLoginModal = () => 
  const  user, handleSignout  = useContext(AuthContext);

  const [showModal, setShowModal] = useState(false);
  const [initialLogin, setInitialLogin] = useState('');

  const noAccept = () => 
    setShowModal(false);
    handleSignout();
  ;

  useEffect(() => 
    if (user) 
      console.log(user.attributes['custom:initiallogin']);
      if (user.attributes['custom:initiallogin'] === 'Yes') 
        setShowModal(true);
      
    
  , [user]);

  const accept = () => 
    updateInitialLogin();
    setShowModal(false);
  ;

  const updateInitialLogin = async () => 
    await Auth.updateUserAttributes(user,  'custom:initiallogin': 'No' );
    setInitialLogin('No');
    setShowModal(false);
  ;

  return (
    <>
      /* Initial login modal */
      <Modal
        show=showModal
        onHide=noAccept
        dialogClassName="modal-70w modal-item"
        aria-labelledby="Initial Login Modal"
      >
        <Modal.Header closeButton>
          <Modal.Title>
            <Image
              src=imgLogo
              
              fluid
              className="modal-image-center"
            />
          </Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <p>
            Text that I must agree to.
          </p>
        </Modal.Body>
        <Modal.Footer>
          <Button onClick=accept>Next</Button>
        </Modal.Footer>
      </Modal>
    </>
  );
;

export default InitialLoginModal;

使用AmplifyAuth.js

import  useReducer, useState, useEffect  from 'react';
import  Auth, Hub  from 'aws-amplify';

const amplifyAuthReducer = (state, action) => 
  switch (action.type) 
    case 'FETCH_USER_DATA_INIT':
      return 
        ...state,
        isLoading: true,
        isError: false
      ;
    case 'FETCH_USER_DATA_SUCCESS':
      return 
        ...state,
        isLoading: false,
        isError: false,
        user: action.payload.user
      ;
    case 'FETCH_USER_DATA_FAILURE':
      return  ...state, isLoading: false, isError: true ;
    case 'RESET_USER_DATA':
      return  ...state, user: null ;
    default:
      throw new Error();
  
;

const useAmplifyAuth = () => 
  const initialState = 
    isLoading: true,
    isError: false,
    user: null
  ;
  const [state, dispatch] = useReducer(amplifyAuthReducer, initialState);
  const [triggerFetch, setTriggerFetch] = useState(false);
  useEffect(() => 
    let isMounted = true;
    const fetchUserData = async () => 
      if (isMounted) 
        dispatch( type: 'FETCH_USER_DATA_INIT' );
      
      try 
        if (isMounted) 
          const data = await Auth.currentAuthenticatedUser();
          if (data) 
            dispatch(
              type: 'FETCH_USER_DATA_SUCCESS',
              payload:  user: data 
            );
          
        
       catch (error) 
        if (isMounted) 
          dispatch( type: 'FETCH_USER_DATA_FAILURE' );
        
      
    ;
    const HubListener = () => 
      Hub.listen('auth', data => 
        const  payload  = data;
        onAuthEvent(payload);
      );
    ;
    const onAuthEvent = payload => 
      switch (payload.event) 
        case 'signIn':
          if (isMounted) 
            setTriggerFetch(true);
            console.log('signed in');
          
          break;
        default:
          return;
      
    ;
    HubListener();
    fetchUserData();
    return () => 
      Hub.remove('auth');
      isMounted = false;
    ;
  , [triggerFetch]);
  const handleSignout = async () => 
    try 
      console.log('signed out');
      await Auth.signOut();
      setTriggerFetch(false);
      dispatch( type: 'RESET_USER_DATA' );
     catch (error) 
      console.error('Error signing out user ', error);
    
  ;
  return  state, handleSignout ;
;
export default useAmplifyAuth;

最后,我只需要用户能够同意这些条款,更新他们的自定义属性,之后不再显示模态框。任何帮助,将不胜感激。谢谢。

@vencovsky 建议后的新 InitialLoginModal.js
import React,  useContext, useEffect, useState  from 'react';
import  Modal, Button, Image  from 'react-bootstrap';
import  Auth  from 'aws-amplify';
import imgLogo from '../img/logo.jpg';
import  AuthContext  from '../context/AuthContext';

const InitialLoginModal = () => 
  const 
    user,
    handleSignout,
    shouldShowModal,
    setShouldShowModal
   = useContext(AuthContext);

  const [showModal, setShowModal] = useState(shouldShowModal);
  // const [initialLogin, setInitialLogin] = useState('');

  const noAccept = () => 
    setShowModal(false);
    handleSignout();
  ;

  useEffect(() => 
    if (user) 
      if (user.attributes['custom:initiallogin'] === 'Yes') 
        setShowModal(true);
      
    
  , [user, setShouldShowModal]);

  const accept = () => 
    updateInitialLogin();
    // setShouldShowModal(false);
  ;

  const updateInitialLogin = async () => 
    if (user) 
      await Auth.updateUserAttributes(user,  'custom:initiallogin': 'No' );
      setShowModal(false);
    
  ;

  return (
    <>
      /* Initial login modal */
      <Modal
        show=showModal
        onHide=noAccept
        dialogClassName="modal-70w modal-item"
        aria-labelledby="Initial Login Modal"
      >
        <Modal.Header closeButton>
          <Modal.Title>
            <Image
              src=imgLogo
              
              fluid
              className="modal-image-center"
            />
          </Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <p>
            Info here
          </p>
        </Modal.Body>
        <Modal.Footer>
          <Button onClick=accept>Next</Button>
        </Modal.Footer>
      </Modal>
    </>
  );
;

export default InitialLoginModal;

【问题讨论】:

useAmplifyAuth 中有什么内容? useAmplifyAuth 是用于放大身份验证的自定义挂钩。我会将它的代码添加到帖子中。 【参考方案1】:

我自己也遇到了这个问题,我相信这是需要更新的行:

const data = await Auth.currentAuthenticatedUser();

到:

const data = await Auth.currentAuthenticatedUser(bypassCache: true);

这将直接从 Cognito 检索更新的属性,以便当属性设置为 custom:initiallogin = 'No' 时不会向用户显示模式。

【讨论】:

【参考方案2】:

不确定这是否正确,但您的上下文中应该有一个状态来决定它是否应该显示模态。

function App() 
  const [shouldShowModal, setShouldShowModal] = useState(true) // you can choose if you want true or false
  const 
    state:  user ,
    handleSignout
   = useAmplifyAuth();

  return (
    <>
      <AuthProvider value= user, handleSignout, shouldShowModal, setShouldShowModal >
        <>
          <Header />
          <Routes />
          <InitialLoginModal />
        </>
      </AuthProvider>
    </>
  );

showModal 的默认值应该来自上下文。

const InitialLoginModal = () => 
  const  user, handleSignout, shouldShowModal, setShouldShowModal  = useContext(AuthContext);

  const [showModal, setShowModal] = useState(shouldShowModal );

  ...


然后您可以使用setShouldShowModal 做更多的事情,因此当您获得身份验证时,您可以选择是否显示。

【讨论】:

谢谢@vencovsky。对于其他功能,我使用的是 setShowModal 还是 setShouldShowModal?我已经尝试了这两种方法并将它们混合在一起,但还没有找到让它工作的方法。 For the other functions 是什么意思? InitialLoginModal 中更新模态状态的函数。 您可以将setShowModal 替换为setShouldShowModal。只需在上下文中使用一个,然后您就可以挂载和卸载组件,并且状态仍然存在 我在上面的代码中添加了新的 InitialLoginModal.js,因为它不适合这里。就目前而言,尽管发生了变化,但在刷新页面后仍会出现模式。谢谢!【参考方案3】:

在 cognito AWS 界面中为需要访问数据的应用客户端选择“oauth scopes”下的“profile”。

【讨论】:

以上是关于属性更新后更新用户上下文 - AWS Amplify的主要内容,如果未能解决你的问题,请参考以下文章

updateMany() 后如何获取所有更新文档的值?

为啥有些用户在 AWS ElasticBeanstalk 中部署后没有体验到更新?

带有 Cognito 的 AWS Lambda API 网关 - 如何使用 IdentityId 访问和更新 UserPool 属性?

使用 Expo 更新 APNS 证书后出现 AWS Pinpoint 错误

通过 CloudFormation 部署 AWS UserPool 并更新属性

无法使用 aws-amplify 从 cognito 获取更新的属性及其值