boilerplate 유튜브 강의 시리즈
Blog ReactJS NodeJS #24 LOGIN PAGE (2)
이번 강의에서는 redux를 사용하여 react 앱의 전체 상태를 관리하는 부분이 있는데
redux에 대해 자세히 몰라 내용을 찾아봤다.
(redux의 flow에 관한 자세한 설명은 여기)
위 그림을 보면 redux의 상태변화는 모두 reducer를 통해 이루어지는 것을 볼 수 있다.
reducer에서는 action에 의해 어떤 상태로 변경될지가 결정된다.
action은 기본적으로 아래와 같이 type과 payload를 가지는 객체이다.
{
type: "액션의 종류를 한번에 식별할 수 있는 문자열 혹은 심볼",
payload: "액션의 실행에 필요한 임의의 데이터",
}
dispatch를 통해 action을 reducer에 넘기면 상태가 변경되고
Store가 업데이트 될 때마다 mapStateToProps, mapDispatchToProps 가 실행된다.
mapStateToProps은 Store의 변경된 상태를 props에 반영하고
mapDispatchToProps은 Reducer에 action의 변경 사항을 알리는 역할을 한다.
1. type, action, reducer 작성
actions > types.js
- 액션 종류를 식별하기 위한 문자열
export const LOGIN_USER = 'login_user';
actions > user_actions.js
- 이전 백엔드 서버 강의에서 api/user/* 라고 잘못 작성한 API 경로를 모두 api/users/*로 바꿨다.
- dataToSubmit : {email: 이메일, password: 비밀번호}
- action(type, payload)을 reducer에 넘긴다.
import axios from 'axios';
import {
LOGIN_USER
} from './types';
export function loginUser(dataToSubmit) {
const request = axios.post('/api/users/login', dataToSubmit)
.then(response => response.data);
return {
type: LOGIN_USER,
payload: request,
}
}
reducer > user_reducer.js
- 주의해서 봐야할 것은 바로 두가지이다.
1. 초기상태는 Reducer의 디폴트 인수에서 정의된다
2. 상태가 변할 때 전달된 state는 그 자체의 값으로 대체 되는 것이 아니라, 새로운 것이 합성되는 것처럼 쓰여진다.
import {
LOGIN_USER
} from '../actions/types';
export default function userReducer(state={}, action) {
switch (action.type) {
case LOGIN_USER:
return {...state, loginSuccess: action.payload};
default:
return state;
}
}
2. login 페이지 최종
RegisterLogin > index.js
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {loginUser} from '../../actions/user_actions';
import {Link} from 'react-router-dom';
class RegisterLogin extends Component {
state = {
email : '',
password : '',
error : '',
};
displayError = error => <p>{error}</p>;
handleChange = event => {
this.setState({ [event.target.name]: event.target.value });
}
submitForm = event => {
event.preventDefault();
let dataToSubmit = {
email: this.state.email,
password: this.state.password,
}
if (this.isFormValid(this.state)) {
this.setState({ errors: [] });
this.props.onFormSubmit(dataToSubmit)
.then(response => {
if(response.payload.loginSuccess) {
this.props.history.push('/');
} else {
this.setState({
error: 'Failed to log in, you can check your Email and Password'
})
}
})
} else {
console.error('Form is not valid');
}
}
isFormValid = ({ email, password }) => {
const form = document.getElementsByTagName('form')[0];
if(!form.checkValidity()) {
this.setState({error: 'Email is invalid'});
} else if(!email || !password) {
this.setState({error: 'Form is not valid'})
} else {
return true;
}
}
render() {
return (
<div className="container">
<h2> Login </h2>
<div className="row">
<form className="col s12">
<div className="row">
<div className="input-field col s12">
<input
name="email"
value={this.state.email}
onChange={e => this.handleChange(e)}
id="email"
type="email"
className="validate"
/>
<label htmlFor="email">Email</label>
<span
className="helper-text"
data-error="Type a right type email"
data-success="right"
/>
</div>
</div>
<div className="row">
<div className="input-field col s12">
<input
name="password"
value={this.state.password}
onChange={e => this.handleChange(e)}
id="password"
type="password"
className="validate"
/>
<label htmlFor="password">Password</label>
<span
className="helper-text"
data-error="wrong"
data-success="right"
/>
</div>
</div>
{this.state.error.length > 0 && (
<div>
{this.displayError(this.state.error)}
</div>
)}
<div className="row">
<div className="col 6">
<button
className="btn waves-effect red lighten-2"
type="submit"
name="action"
onClick={this.submitForm}
>
Login
</button>
</div>
<div className="col 6">
<Link to="/register">
<button
className="btn waves-effect red lighten-2"
type="submit"
name="action"
>
Sign up
</button>
</Link>
</div>
</div>
</form>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
user: state.user,
}
}
function mapDispatchToProps(dispatch) {
return {
onFormSubmit: (dataToSubmit) => dispatch(loginUser(dataToSubmit))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(RegisterLogin);
로그인 테스트
'IT > 프로젝트' 카테고리의 다른 글
18. boilerplate - 회원가입 페이지 (0) | 2021.01.04 |
---|---|
16. boilerplate - 로그인 페이지 (1) (0) | 2021.01.02 |
15. boilerplate - redux 세팅 (0) | 2021.01.02 |
14. boilerplate - proxy 설정 및 concurrently 적용 (0) | 2021.01.01 |
13. boilerplate - react router dom (0) | 2021.01.01 |