2017-05-31 7 views
0

Ich bin sehr neu in ReactJS und redux. Ich versuche, eine neue Anwendung basierend auf der folgenden Referenz zu erstellen. https://github.com/shalomeir/snippod-starter-demo-app-frontAktualisierungsstatus in react redux

Jetzt versuche ich den Login-Bereich mit AWS Cognito zu integrieren. Alles ist in Ordnung, wenn ich die herkömmliche Methode zum Erstellen des Anmeldeflusses verwendet habe, aber wenn ich es mit redux zusammengeführt habe, kann ich den Status nicht aktualisieren. Darf ich wissen, ob ich etwas verpasst habe?

Behälter/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> 
    ); 
    } 
} 

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

Behälter/Boden/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> 
    ); 
    } 
} 

Antwort

1

Sie sollten die HOC von @connect()

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

Um den Status zu hören und den Versand zu verwenden, müssen Sie den Status zuordnen und zu den Requisiten Ihrer Komponente versenden. Um dies zu tun, ich ah höhere Ordnung Komponente:

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

zurück exportieren denke ich createSelector Teil in Ground.js die gleichen wie mapStateToProps ist? Die Konsole this.props.auth ist jedoch in LoginDialog.js nicht definiert. – HUNG