2017-05-31 1 views
0

Je suis très nouveau à ReactJS et redux. J'essaye de construire une nouvelle application basée sur la référence suivante. Maintenant, j'essaye d'intégrer la section de connexion avec AWS Cognito. Tout va bien quand j'ai utilisé la méthode traditionnelle pour créer le flux de connexion, mais quand je l'ai fusionné à redux, je ne suis pas capable de mettre à jour l'état. Puis-je savoir si j'ai raté quelque chose?Mettre à jour l'état dans la réaction redux

conteneurs/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> 
    ); 
    } 
} 

canards/authentification/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)); 
} 

conteneurs/sol/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> 
    ); 
    } 
} 

Répondre

1

Vous devez exporter le COH retourné par @connect()

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

Pour écouter l'état et utiliser dispatch, vous devez mapper l'état et l'envoi aux accessoires de votre composant. Pour ce faire, j'utiliser ah composante d'ordre supérieur:

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

Je pense qu'une partie createSelector dans Ground.js est le même que mapStateToProps? Cependant, la console this.props.auth n'est pas définie dans LoginDialog.js. – HUNG