>

ReactJS와 redux를 처음 접했습니다. 다음 참조를 기반으로 새 응용 프로그램을 작성하려고합니다. https://github.com/shalomeir/snippod-starter-demo-app- 앞

이제 로그인 섹션을 AWS Cognito와 통합하려고합니다. 로그인 흐름을 구축하는 전통적인 방법을 사용하면 모든 것이 정상이지만, 그것을 redux에 병합하면 상태를 업데이트 할 수 없습니다. 내가 놓친 부분을 알 수 있습니까?

컨테이너 /DialogWindows/LoginDialog.js

import React, { Component, PropTypes } from 'react';
import Radium from 'radium';
import _ from 'lodash';
import $ from 'jquery';
import classNames from 'classnames';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { reduxForm } from 'redux-form';
import { defineMessages, FormattedMessage } from 'react-intl';
import { showLoginDialog, showRegisterDialog,
  closeDialog, redirectReplacePath, reloadPage } from 'ducks/application/application';
import { loginSuccess, login } from 'ducks/authentication/auth';
import { facebook, aws } from 'constants/config';
import FacebookLogin from './FacebookLogin';
import TwitterLogin from './TwitterLogin';
import { Link } from 'react-router';
//Do not connect this action
import { switchLangAndDeleteLanguageQuery } from 'ducks/application/application';
import { showDelayedToastMessage } from 'ducks/messages/toastMessage';
import toastMessages from 'i18nDefault/toastMessages';
import loginValidation from './loginValidation';
import { CognitoUserPool, CognitoUserAttribute, CognitoUser, AuthenticationDetails } from 'amazon-cognito-identity-js';
const styles = require('./DialogStyles');
@connect(
  null,
  { showRegisterDialog, closeDialog, redirectReplacePath, reloadPage }
)
@reduxForm({
  form: 'login',
  fields: ['emailId', 'password'],
  validate: loginValidation
})
@Radium
export default class LoginDialog extends Component {
  static propTypes = {
    //auth: PropTypes.object.isRequired,
    redirectReplacePath: PropTypes.func.isRequired,
    showRegisterDialog: PropTypes.func.isRequired,
    closeDialog: PropTypes.func.isRequired,
    reloadPage: PropTypes.func.isRequired,
    fields: PropTypes.object.isRequired,
    error: PropTypes.string,
    errors: PropTypes.object.isRequired,
    handleSubmit: PropTypes.func.isRequired,
    initializeForm: PropTypes.func.isRequired,
    invalid: PropTypes.bool.isRequired,
    dirty: PropTypes.bool.isRequired,
    submitting: PropTypes.bool.isRequired,
    values: PropTypes.object.isRequired
  };
  constructor() {
    super();
    this.state = { changed: false };
    this._onSubmit = this._onSubmit.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    if (!_.isEqual(this.props.values, nextProps.values) && !this.state.changed && nextProps.dirty) {
      this.setState({ changed: true });
    }
  }
  componentWillMount(dispatch) {
    //Use case 16    
    var userPool = new CognitoUserPool(aws.cognito);
    var cognitoUser = userPool.getCurrentUser();
    if (cognitoUser != null) {
        cognitoUser.getSession(function(err, session) {
            if (err) {
                alert(err);
                return;
            }
            console.log('session validity: ' + session.isValid());
            //dispatch(loginSuccess());
            const credentialsPath = 'cognito-idp.' + aws.cognito.region + '.amazonaws.com/' + aws.cognito.UserPoolId;
            // NOTE: getSession must be called to authenticate user before calling getUserAttributes
            cognitoUser.getUserAttributes(function(err, attributes) {
                if (err) {
                    // Handle error
                } else {
                    // Do something with attributes
                }
            });
            AWS.config.credentials = new AWS.CognitoIdentityCredentials({
                IdentityPoolId : aws.cognito.IdentityPoolId,
                Logins : {
                    // Change the key below according to the specific region your user pool is in.
                    credentialsPath : session.getIdToken().getJwtToken()
                }
            });
            // Instantiate aws sdk service objects now that the credentials have been updated.
            // example: var s3 = new AWS.S3();
            // return function(dispatch){
            //     dispatch(loginSuccess());
            // };
            return (dispatch, getState) => {
              //console.log(getState().application.lang);
              return dispatch({
                types: LOGIN_SUCCESS,
              });
            };
        });
    }
  }
  _onSubmit(values, dispatch) {
    this.props.initializeForm();
    return new Promise((resolve, reject) => {
        dispatch(
          login(values)
        ).then((response) => {
          //const account = response.entities.accounts[response.result];
          this.props.reloadPage();
          //dispatch(switchLangAndDeleteLanguageQuery(account.language.split('-')[0]));
          // dispatch(showDelayedToastMessage({
          //   type: 'info',
          //   title: toastMessages.loginTitle,
          //   body: Object.assign(toastMessages.loginBody, { values: { username: account.username } })
          // }, 300));
          this.props.redirectReplacePath();
          resolve(response);
        }).catch((error) => {
          reject({ _error: error.message });
        });
    });
  }
  render() {
    const { error, errors, fields: { emailId, password }, handleSubmit, invalid,
      submitting } = this.props;
    const { changed } = this.state;
    return (
      <div className="login ui text container main-container">
        <img src="/images/logo.png" className="ui centered image" />
          <form className={classNames('ui form login-form one column stackable center aligned page grid', { 'error': (invalid && changed) })} onSubmit={handleSubmit(this._onSubmit)}>
            <div className="ui grid inner">
              <div className="ui segment attached top">SIGN IN</div>
              <div className="ui segment attached social-login">
                <FacebookLogin />
                <TwitterLogin />
              </div>
              <div className="ui attached segment cognito-login">
                <div className={classNames('field', { 'error': (emailId.invalid && changed) })}>
                  <label>EMAIL ADDRESS <span className="red">*</span></label>
                  <div className="ui left icon email input">
                    {/*<i className="user icon" />*/}
                    <input type="email" name="emailId" placeholder="Your Email" ref="emailId" {...emailId} />
                  </div>
                  <div className="ui email pointing red basic small label transition hidden" style={styles.errorText}>
                    {errors.emailId ? <FormattedMessage {...errors.emailId} /> : null}
                  </div>
                </div>
                <div className={classNames('field', { 'error': (password.invalid && changed) })}>
                  <div className="ui grid float">
                    <div className="two column row field">
                      <label className="left floated column">YOUR PASSWORD <span className="red">*</span></label>
                      <Link to="/forgetpassword" className="right floated column">Forgotten password?</Link>
                    </div>
                  </div>
                  <div className="ui left icon password input">
                    {/*<i className="lock icon" />*/}
                    <input type="password" name="password" placeholder="Password" ref="password" {...password} />
                  </div>
                  <div className="ui password pointing red basic small label transition hidden" style={styles.errorText}>
                    {errors.password ? <FormattedMessage {...errors.password} /> : null}
                  </div>
                </div>
                <button type="submit" className={classNames('ui fluid large blue button', { 'loading': submitting })}
                        disabled={submitting || invalid} >
                  {/*<FormattedMessage {...i18n.button} />*/}
                  SIGN IN
                </button>
                <div className="field">
                  <div className="ui checkbox">
                    <input type="checkbox" tabIndex="0" className="hidden" name="checkbox1" id="checkbox1" />
                    <label htmlFor="checkbox1">Remember Me</label>
                  </div>
                </div>
              </div>
              <div id="login-general-error-message" className="ui general error message hidden" style={styles.errorText}>
                {error}
              </div>
            </div>
          </form>
      </div>
    );
  }
}

dcks/authentication/auth.js

const debug = require('utils/getDebugger')('auth');
import { switchLangAndDeleteLanguageQuery, reloadPage, pushPath } from 'ducks/application/application';
import { showDelayedToastMessage } from 'ducks/messages/toastMessage';
import toastMessages from 'i18nDefault/toastMessages';
import Schemas from 'ducks/Schemas';
import { facebook, aws } from 'constants/config';
//import { AWS } from 'aws-sdk';
import { CognitoUserPool, CognitoUserAttribute, CognitoUser, AuthenticationDetails } from 'amazon-cognito-identity-js';
const LOAD = 'authentication/auth/LOAD';
const LOAD_SUCCESS = 'authentication/auth/LOAD_SUCCESS';
const LOAD_FAIL = 'authentication/auth/LOAD_FAIL';
const LOGIN = 'authentication/auth/LOGIN';
const LOGIN_SUCCESS = 'authentication/auth/LOGIN_SUCCESS';
const LOGIN_FAIL = 'authentication/auth/LOGIN_FAIL';
const initialState = {
  loggedIn: false,
  loaded: false,
  account: null,
  error: null
};
export default function reducer(state = initialState, action = {}) {
  const { INIT_ALL_STATE } = require('ducks/globalActions');
  switch (action.type) {
    case LOAD:
      return state;
    case LOAD_SUCCESS:
      if (action.response) {
        return {
          ...state,
          loggedIn: true,
          loaded: true,
          account: action.response.entities.accounts[action.response.result],
        };
      }
      return {
        ...state,
        loggedIn: false,
        loaded: true,
        error: null
      };
    case LOAD_FAIL:
      return {
        ...state,
        loading: false,
        loaded: true,
        error: action.error
      };
    case LOGIN:
      return state;
    case LOGIN_SUCCESS:
      return {
        ...state,
        loggedIn: true,
        account: action.response.entities.accounts[action.response.result]
      };
    case LOGIN_FAIL:
      return {
        ...state,
        loggedIn: false,
        account: null,
        error: action.error
      };
    case INIT_ALL_STATE:
      return initialState;
    default:
      return state;
  }
}
export function loginSuccess() {
  return { type: LOGIN_SUCCESS };
}
export function isLoaded(globalState) {
  return globalState.auth && globalState.auth.loaded;
}
export function load() {
  return {
    types: [LOAD, LOAD_SUCCESS, LOAD_FAIL],
    promise: (client) => client.get('/auth/load_auth/', {
      schema: Schemas.MY_ACCOUNT
    })
  };
}
export function login(loginForm) {
  // return (dispatch, getState) => {
  //   return dispatch({
  //     types: [LOGIN, LOGIN_SUCCESS, LOGIN_FAIL],
  //     promise: (client) => client.post('/auth/login/', {
  //       data: {
  //         email: loginForm.emailId,
  //         password: loginForm.password
  //       },
  //       params: {
  //         language: getState().application.lang
  //       },
  //       schema: Schemas.MY_ACCOUNT
  //     })
  //   });
  // };
//Use case 4, 23
    var authenticationData = {
        Username : loginForm.emailId,
        Password : loginForm.password,
    };
    var authenticationDetails = new AuthenticationDetails(authenticationData);
    var userPool = new CognitoUserPool(aws.cognito);
    var userData = {
        Username : loginForm.emailId,
        Pool : userPool
    };
    var cognitoUser = new CognitoUser(userData); 
    console.log(authenticationDetails);
    console.log("Username: " + authenticationData.Username + " Password: " + authenticationData.Password );
    cognitoUser.authenticateUser(authenticationDetails, {
        onSuccess: function (result) {
            console.log('access token + ' + result.getAccessToken().getJwtToken());
            /*Use the idToken for Logins Map when Federating User Pools with Cognito Identity or when passing through an Authorization Header to an API Gateway Authorizer*/
            console.log('idToken + ' + result.idToken.jwtToken);
            const credentialsPath = 'cognito-idp.' + aws.cognito.region + '.amazonaws.com/' + aws.cognito.UserPoolId;
            AWS.config.credentials = new AWS.CognitoIdentityCredentials({
                IdentityPoolId : aws.cognito.IdentityPoolId, // your identity pool id here
                Logins : {
                    // Change the key below according to the specific region your user pool is in.
                    credentialsPath : result.getIdToken().getJwtToken()
                }
            });
            return { type: LOGIN_SUCCESS }
            //return (dispatch, getState) => {
              // return dispatch({
              //   types: LOGIN_SUCCESS,
              // });
            //};
        },
        onFailure: function(err) {
            alert(err);
            return { type: LOGIN_SUCCESS }
        },
        newPasswordRequired: function(userAttributes, requiredAttributes) {
            // User was signed up by an admin and must provide new
            // password and required attributes, if any, to complete
            // authentication.
            // the api doesn't accept this field back
            delete userAttributes.email_verified;
            // Get these details and call
            cognitoUser.completeNewPasswordChallenge(loginForm.password, userAttributes, this);
        }
    });
}
// thunk action that dispatch login action and then dispatch follow action such as switch lang.
// TODO: Check return response or error. This is not use. Instead, login process is handled in react login dialog.
export function loginAndFollow(loginForm) {
  return (dispatch, getState) => {
    dispatch(
      login(loginForm)
    ).then((response) => {
  //     const account = response.entities.accounts[response.result];
  //     dispatch(switchLangAndDeleteLanguageQuery(account.language.split('-')[0]));
  //     dispatch(showDelayedToastMessage({
  //       type: 'info',
  //       title: toastMessages.loginTitle,
  //       body: Object.assign(toastMessages.loginBody, { values: { username: account.username } })
  //     }, 500));
       return response;
    }).catch((error) => {
      debug('Error occurred : ', error);
      return error;
    });
  };
    return dispatch(login(loginForm));
}

컨테이너 /Ground/Ground.js

import React, { Component, PropTypes } from 'react';
import Radium from 'radium';
import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { defineMessages, FormattedMessage } from 'react-intl';
import { showLoginDialog, showRegisterDialog, redirectReplacePath } from 'ducks/application/application';
import {
  LoginDialog,
  RegisterDialog
} from 'containers';
import { DialogWindow } from 'layout/DialogWindow/DialogWindow';
@connect(
  createSelector([
    state => state.auth,
    state => state.application
  ], (auth, application) => {
    return { auth, application };
  }),
  { showLoginDialog, showRegisterDialog, redirectReplacePath, DialogWindow }
)
@Radium
export default class Ground extends Component {
  static propTypes = {
    location: PropTypes.object.isRequired,
    auth: PropTypes.object.isRequired,
    application: PropTypes.object.isRequired,
    showLoginDialog: PropTypes.func.isRequired,
    showRegisterDialog: PropTypes.func.isRequired,
    redirectReplacePath: PropTypes.func.isRequired
  };
  constructor() {
    super();
    this.checkAuth = this.checkAuth.bind(this);
  }
  componentWillMount() {
    const redirect = this.checkAuth();
    if (!redirect) {
      if (this.props.location.pathname === '/login') {
        this.props.showLoginDialog();
        this.setState({ page: 'login' });
      }
      if (this.props.location.pathname === '/register') {
        this.props.showRegisterDialog();
        this.setState({ page: 'register' });
      }
    }
  }
  componentWillReceiveProps(nextProps) {
    if (!this.props.auth.loggedIn && nextProps.auth.loggedIn) {
      this.props.redirectReplacePath('/');
    }
  }
  checkAuth() {
    //console.log('hello will login check auth');
    if (this.props.auth.loggedIn) {
      // You already logged in, so do not needed to be here!
      this.props.redirectReplacePath('/');
      return true;
    }
    return false;
  }
  render() {
    const messageHeader = this.state.page === 'login' ? i18n.loginMessageHeader : i18n.registerMessageHeader;
    const messageBody = this.state.page === 'login' ? i18n.loginMessageBody : i18n.registerMessageBody;
    const { auth, application } = this.props;
    let content = null;
    // content = <DialogWindow auth={this.props.auth} application={this.props.application} />;
    if (this.state.page === 'login') {
      content = <LoginDialog />;
    } else if (this.state.page === 'register') {
      content = <RegisterDialog />;
    }
    return (
      <div className="loading ui text container main-container">
        {content}
        {/*<Helmet title={this.state.page === 'login' ? 'Login' : 'Register'} />
        <div className="ui message">
          <div className="header">
            <FormattedMessage {...messageHeader} />
          </div>
          <p><FormattedMessage {...messageBody} /></p>
        </div>*/}
      </div>
    );
  }
}


  • 답변 # 1

    @connect() 가 리턴 한 HOC를 내 보내야합니다.

    export default connect(
         null,
         { showRegisterDialog,
           closeDialog,
           redirectReplacePath,
           reloadPage
         }
    )
    
    

  • 답변 # 2

    상태를 듣고 디스패치를 ​​사용하려면 상태를 매핑하고 컴포넌트의 prop에 디스패치해야합니다. 이 작업을 수행하려면 ah 고차 구성 요소를 사용하십시오.

    import { connect } from 'react-redux';
    import MyComponent from '../components/MyComponent';
    import { updateFoo } from '../redux/actions';
    //Listen to the object 'foo' in the state. It is now accessible through 'this.props.foo' 
    const mapStateToProps = state => {
      return {
        foo: state.foo,
      };
    };
    //Map the actions to the props. Now accessible through 'this.props.updateFoo()'
    const mapDispatchToProps = dispatch => {
      return {
        updateFoo: foo => dispatch(updateFoo(foo)),
      };
    };
    export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
    
    

관련 자료

  • 이전 excel - 단일 열의 데이터를 여러 열의 열 단위 vba로 분할
  • 다음 html5 - 아래 CSS를 더 단순화 할 수 있습니까?