【问题标题】:Update User Context After Attribute Update - AWS Amplify属性更新后更新用户上下文 - 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}
              alt="Logo"
              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}
              alt="Logo"
              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 是用于放大身份验证的自定义挂钩。我会将它的代码添加到帖子中。

标签: javascript reactjs amazon-web-services aws-amplify react-context


【解决方案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”。

    【讨论】:

      猜你喜欢
      • 2020-06-26
      • 2019-05-08
      • 2022-01-21
      • 1970-01-01
      • 2019-04-21
      • 2012-04-14
      • 2020-11-12
      • 1970-01-01
      • 2020-05-29
      相关资源
      最近更新 更多