2017-05-31 99 views
0

我对ReactJS和redux非常新颖。 我正在尝试基于以下参考来构建新的应用程序。 https://github.com/shalomeir/snippod-starter-demo-app-front更新状态反应减少

现在,我试图将登录部分与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> 
    ); 
    } 
} 

鸭/认证/ 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.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()

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

要收听状态并使用分派,必须将状态和分派映射到组件的道具。 要做到这一点,我用啊高阶组件:

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); 
+0

返回HOC我觉得Ground.js createSelector部分是一样的mapStateToProps?但是,console.props.auth在LoginDialog.js中未定义。 – HUNG