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

时间:2019-08-08 12:29:22

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

我有一个要求,第一次用户登录后会看到一个他们需要同意的弹出窗口。我在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;

useAmplifyAuth.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;

3 个答案:

答案 0 :(得分:1)

我本人只是遇到了这个问题,我认为这是需要更新的行:

<?php
namespace REDACTED\REDACTEDVeranstaltungen\Routing\Aspect;

use TYPO3\CMS\Core\Routing\Aspect\PersistedMappableAspectInterface;
use TYPO3\CMS\Core\Site\SiteLanguageAwareTrait;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Database\ConnectionPool;

class TerminValueMapper implements PersistedMappableAspectInterface
{
    use SiteLanguageAwareTrait;

    /**
     * @param string $value
     *
     * @return string|null
     */
    public function generate(string $value): ?string
    {
        if($uid=intval($value)){

            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_REDACTEDveranstaltungen_domain_model_termin');
            $statement = $queryBuilder
               ->select('t.uid', 'v.titel', 'v.urltitel', 't.beginn')
               ->from('tx_REDACTEDveranstaltungen_domain_model_termin','t')
               ->leftJoin('t', 'tx_REDACTEDveranstaltungen_domain_model_veranstaltung', 'v', 't.veranstaltung = v.uid')
               ->where(
                  $queryBuilder->expr()->eq('t.uid', $uid)//$queryBuilder->createNamedParameter('horst')
               )
               ->execute();

            if($record = $statement->fetch()){
                if(is_array($record) && mb_strlen(trim($record['urltitel']))){
                    $beginn = new \DateTime();
                    $beginn->setTimestamp(intval($record['beginn']));
                    return $uid.'--'.str_replace('--', '-', $record['urltitel'].'-'.$beginn->format('d-m-Y') );
                }
            }
        }
        return $value;
    }

    /**
     * @param string $value
     *
     * @return string|null
     */
    public function resolve(string $value): ?string
    {
        return intval(explode('--',$value)[0]);
    }
}

收件人:

routeEnhancers:
  REDACTEDveranstaltungen_veranstaltungen:
    type: Extbase
    extension: REDACTEDVeranstaltungen
    plugin: Veranstaltungen
    routes: 
      - { routePath: '/themenbereiche/{thema_titel}', _controller: 'Termin::search',  _arguments: {'thema_titel': 'veranstaltungsarten/0'} }
      - { routePath: '/veranstaltung/{termin_titel}', _controller: 'Veranstaltung::show',  _arguments: {'termin_titel': 'termin'} }
    aspects:
      termin_titel:
        #type: PersistedAliasMapper
        #tableName: 'tx_REDACTEDveranstaltungen_domain_model_termin'
        #routeFieldName: 'uid'
        type: TerminValueMapper
      thema_titel:
        type: PersistedAliasMapper
        tableName: 'sys_category'
        routeFieldName: 'title'

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

答案 1 :(得分:0)

不确定这是否正确,但是您应该在上下文中确定一个状态,以决定是否应显示模式。

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做更多的事情,因此在获得身份验证时,您可以选择是否显示该身份。

答案 2 :(得分:0)

在cognito AWS界面的“ oauth范围”下为需要访问数据的应用程序客户端选择“配置文件”。