홈>
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
- 답변 # 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);
관련 자료
- javascript - 체크 박스의 상태 지연 업데이트, React
- reactjs - Redux State를 React Component에 전달할 때 무한 루프
- javascript - JSON 데이터로 업데이트 상태 변수 반응
- javascript - typeerror - 반응 및 redux에서 상태를 반복 할 수 없습니다
- reactjs - React Redux에서 전역 및 로컬 상태를 혼합하는 방법
- javascript - React의 while 루프 내부에서 State를 어떻게 업데이트합니까?
- reactjs - react 및 redux - useeffect에서 api 호출 후 상태 변이 처리
- reactjs - React Native는 상태를 업데이트하지 않습니다
- reactjs - React Hooks를 사용하여 객체로 상태 업데이트
- javascript - 후크에서 반응에서 상태를 올바르게 업데이트하는 방법은 무엇입니까?
- reactjs - axiosget 메서드를 사용하여 react에서 상태를 업데이트하려면 어떻게해야합니까?
- reactjs - 업데이트 상태를 동 기적으로 반응
- reactjs - 반응/redux - 다른 경로로 직접 이동할 때 상태 손실
- reactjs - redux에서 상태를 업데이트하지 않는 반응
- reactjs - redux 상태가 업데이트되면 어떻게 업데이트가 렌더링됩니까?
- reactjs - 반응 상태에서 배열 업데이트가 작동하지 않음
- reactjs - 내 redux 저장소의 데이터에 액세스 할 수 없습니다
- reactjs - 네이티브 반응 - 소비자의 컨텍스트 업데이트는 처음에만 작동합니다
- javascript - for 루프 내부의 setState가 상태를 올바르게 업데이트하지 않습니다
- 상태가 변경되면 React Native의 경고가 사라집니다
관련 질문
- reactjs : react /redux에서 페이지를 새로 고치는 동안 백엔드에 대한 비동기 API 호출을 처리하는 방법은 무엇입니까?
- node.js : 이 응용 프로그램을 구축하려면 어떻게해야합니까?
- javascript : Redux 상태는 디스패치없이 앞뒤로 계속 변경됩니다.
- reactjs : 무한 새로 고침 루프를 유발하는 Redux
- javascript : React-여러 데이터로 소품을 재정의하는 useEffect
- javascript : useSelector를 사용하여 redux에서 데이터를 가져올 때 정의되지 않은 데이터를 피하는 방법은 무엇입니까?
- javascript : 때때로 "match.map이 함수가 아닙니다"라는 오류가 발생합니다-반응에서
- javascript : Redux saga 값은 즉시 적용하거나 사용할 수 없습니다.
- reactjs : React에서 레거시 code를 어떻게 다시 작성해야합니까?
- reactjs : 대기열처럼 생성하고 첫 번째 redux-saga 생성기 효과가 끝날 때까지 기다린 다음 일부 대기열에서 다음을 가져 오는 Redux Saga가 있습니까?
@connect()
가 리턴 한 HOC를 내 보내야합니다.