From 160a747c8a5581ef731966ab778b2c42598e4df8 Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 18:00:11 -0300 Subject: [PATCH 01/24] Folder structure changed --- .../LoginForm => assets}/LoginForm.css | 0 src/components/LoginForm.js | 76 +++++++++++++++++++ src/components/LoginForm/LoginForm.js | 67 ---------------- src/containers/Login.js | 0 src/index.js | 12 +-- 5 files changed, 82 insertions(+), 73 deletions(-) rename src/{components/LoginForm => assets}/LoginForm.css (100%) create mode 100644 src/components/LoginForm.js delete mode 100644 src/components/LoginForm/LoginForm.js create mode 100644 src/containers/Login.js diff --git a/src/components/LoginForm/LoginForm.css b/src/assets/LoginForm.css similarity index 100% rename from src/components/LoginForm/LoginForm.css rename to src/assets/LoginForm.css diff --git a/src/components/LoginForm.js b/src/components/LoginForm.js new file mode 100644 index 0000000..650a169 --- /dev/null +++ b/src/components/LoginForm.js @@ -0,0 +1,76 @@ +import React, {Component} from 'react' +import {connect} from 'react-redux' +import {login} from '../redux/reducer' +import '../assets/LoginForm.css' + +class LoginForm extends Component { + constructor(props) { + super(props) + this.state = {} + this.onSubmit = this.onSubmit.bind(this) + } + + render() { + let {email, password} = this.state + let {isLoginPending, isLoginSuccess, loginError} = this.props + return ( +
+
+
+ + this.setState({email: e.target.value})} + value={email} + /> +
+ +
+ + this.setState({password: e.target.value})} + value={password} + /> +
+
+ + + +
+ {isLoginPending &&
Please wait...
} + {isLoginSuccess &&
Success.
} + {loginError &&
{loginError.message}
} +
+
+ ) + } + + onSubmit(e) { + e.preventDefault() + let {email, password} = this.state + this.props.login(email, password) + this.setState({ + email: '', + password: '' + }) + } +} + +const mapStateToProps = state => { + return { + isLoginPending: state.isLoginPending, + isLoginSuccess: state.isLoginSuccess, + loginError: state.loginError + } +} + +const mapDispatchToProps = dispatch => { + return { + login: (email, password) => dispatch(login(email, password)) + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(LoginForm) diff --git a/src/components/LoginForm/LoginForm.js b/src/components/LoginForm/LoginForm.js deleted file mode 100644 index 915333b..0000000 --- a/src/components/LoginForm/LoginForm.js +++ /dev/null @@ -1,67 +0,0 @@ -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { login } from '../../redux/reducer'; -import './LoginForm.css'; - -class LoginForm extends Component { - - constructor(props) { - super(props); - this.state = {}; - this.onSubmit = this.onSubmit.bind(this); - } - - render() { - let {email, password} = this.state; - let {isLoginPending, isLoginSuccess, loginError} = this.props; - return ( -
-
-
- - this.setState({email: e.target.value})} value={email}/> -
- -
- - this.setState({password: e.target.value})} value={password}/> -
-
- - - -
- { isLoginPending &&
Please wait...
} - { isLoginSuccess &&
Success.
} - { loginError &&
{loginError.message}
} -
-
- ) - } - - onSubmit(e) { - e.preventDefault(); - let { email, password } = this.state; - this.props.login(email, password); - this.setState({ - email: '', - password: '' - }); - } -} - -const mapStateToProps = (state) => { - return { - isLoginPending: state.isLoginPending, - isLoginSuccess: state.isLoginSuccess, - loginError: state.loginError - }; -} - -const mapDispatchToProps = (dispatch) => { - return { - login: (email, password) => dispatch(login(email, password)) - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(LoginForm); \ No newline at end of file diff --git a/src/containers/Login.js b/src/containers/Login.js new file mode 100644 index 0000000..e69de29 diff --git a/src/index.js b/src/index.js index 813f834..3ddd965 100644 --- a/src/index.js +++ b/src/index.js @@ -1,12 +1,12 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import LoginForm from './components/LoginForm/LoginForm'; -import { Provider } from 'react-redux'; -import store from './redux/store'; +import React from 'react' +import ReactDOM from 'react-dom' +import LoginForm from './components/LoginForm' +import {Provider} from 'react-redux' +import store from './redux/store' ReactDOM.render( , document.getElementById('root') -); +) From 54ac45c38aa013f7af28a2221db85108fc1c29e5 Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 18:05:26 -0300 Subject: [PATCH 02/24] method onSubmit changed to handleSubmit (its a convention) --- src/components/LoginForm.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/LoginForm.js b/src/components/LoginForm.js index 650a169..0d940dc 100644 --- a/src/components/LoginForm.js +++ b/src/components/LoginForm.js @@ -7,14 +7,24 @@ class LoginForm extends Component { constructor(props) { super(props) this.state = {} - this.onSubmit = this.onSubmit.bind(this) + this.handleSubmit = this.handleSubmit.bind(this) + } + + handleSubmit(e) { + e.preventDefault() + let {email, password} = this.state + this.props.login(email, password) + this.setState({ + email: '', + password: '' + }) } render() { let {email, password} = this.state let {isLoginPending, isLoginSuccess, loginError} = this.props return ( -
+
@@ -47,16 +57,6 @@ class LoginForm extends Component { ) } - - onSubmit(e) { - e.preventDefault() - let {email, password} = this.state - this.props.login(email, password) - this.setState({ - email: '', - password: '' - }) - } } const mapStateToProps = state => { From ed1644a20b67e15841f37ae3623be4d35d33f2cc Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 18:06:57 -0300 Subject: [PATCH 03/24] handleSubmit now its an arrow function and its not needed to bind --- src/components/LoginForm.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/LoginForm.js b/src/components/LoginForm.js index 0d940dc..45e96cb 100644 --- a/src/components/LoginForm.js +++ b/src/components/LoginForm.js @@ -7,10 +7,9 @@ class LoginForm extends Component { constructor(props) { super(props) this.state = {} - this.handleSubmit = this.handleSubmit.bind(this) } - handleSubmit(e) { + handleSubmit = e => { e.preventDefault() let {email, password} = this.state this.props.login(email, password) From f6177c8e4093d7209f67fcf177fd0f521bfd37dd Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 18:11:21 -0300 Subject: [PATCH 04/24] method handleFieldChange added, its not good change state from render method. with this, there's no more the warning 'Warning: LoginForm is changing an uncontrolled input of type email to be controlled.' --- src/components/LoginForm.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/components/LoginForm.js b/src/components/LoginForm.js index 45e96cb..6736716 100644 --- a/src/components/LoginForm.js +++ b/src/components/LoginForm.js @@ -6,7 +6,10 @@ import '../assets/LoginForm.css' class LoginForm extends Component { constructor(props) { super(props) - this.state = {} + this.state = { + email: '', + password: '' + } } handleSubmit = e => { @@ -19,6 +22,12 @@ class LoginForm extends Component { }) } + handleFieldChange = e => { + this.setState({ + [e.target.name]: e.target.value + }) + } + render() { let {email, password} = this.state let {isLoginPending, isLoginSuccess, loginError} = this.props @@ -30,7 +39,7 @@ class LoginForm extends Component { this.setState({email: e.target.value})} + onChange={this.handleFieldChange} value={email} />
@@ -40,7 +49,7 @@ class LoginForm extends Component { this.setState({password: e.target.value})} + onChange={this.handleFieldChange} value={password} />
From 24b0a8889be83d3767ed54d79e50b79fcd45c301 Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 19:42:12 -0300 Subject: [PATCH 05/24] ES6 sintax improvements --- src/components/LoginForm.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/components/LoginForm.js b/src/components/LoginForm.js index 6736716..5111cef 100644 --- a/src/components/LoginForm.js +++ b/src/components/LoginForm.js @@ -67,18 +67,14 @@ class LoginForm extends Component { } } -const mapStateToProps = state => { - return { - isLoginPending: state.isLoginPending, - isLoginSuccess: state.isLoginSuccess, - loginError: state.loginError - } -} +const mapStateToProps = state => ({ + isLoginPending: state.isLoginPending, + isLoginSuccess: state.isLoginSuccess, + loginError: state.loginError +}) -const mapDispatchToProps = dispatch => { - return { - login: (email, password) => dispatch(login(email, password)) - } -} +const mapDispatchToProps = dispatch => ({ + login: (email, password) => dispatch(login(email, password)) +}) export default connect(mapStateToProps, mapDispatchToProps)(LoginForm) From be4c671b48073b869b102693513c72b21dfb368b Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 19:56:06 -0300 Subject: [PATCH 06/24] redux-actions added --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 78afb16..0df195f 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "react-dom": "^15.5.4", "react-redux": "^5.0.4", "redux": "^3.6.0", + "redux-actions": "^2.0.2", "redux-logger": "^3.0.1", "redux-thunk": "^2.2.0" }, From bccaa5e302867445eeba9abcee0d666df00c77ba Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 19:56:24 -0300 Subject: [PATCH 07/24] refactoring to redux-actions --- src/redux/reducer.js | 92 ++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 51 deletions(-) diff --git a/src/redux/reducer.js b/src/redux/reducer.js index d958b37..5a85a40 100644 --- a/src/redux/reducer.js +++ b/src/redux/reducer.js @@ -1,77 +1,67 @@ -const SET_LOGIN_PENDING = 'SET_LOGIN_PENDING'; -const SET_LOGIN_SUCCESS = 'SET_LOGIN_SUCCESS'; -const SET_LOGIN_ERROR = 'SET_LOGIN_ERROR'; +import {createAction} from 'redux-actions' + +const SET_LOGIN_PENDING = 'modules/Login/PENDING' +const SET_LOGIN_SUCCESS = 'modules/Login/SUCCESS' +const SET_LOGIN_ERROR = 'modules/Login/ERROR' + +const setLoginPending = createAction(SET_LOGIN_PENDING) +const setLoginSuccess = createAction(SET_LOGIN_SUCCESS) +const setLoginError = createAction(SET_LOGIN_ERROR) export function login(email, password) { return dispatch => { - dispatch(setLoginPending(true)); - dispatch(setLoginSuccess(false)); - dispatch(setLoginError(null)); + dispatch(setLoginPending(true)) + dispatch(setLoginSuccess(false)) + dispatch(setLoginError(null)) callLoginApi(email, password, error => { - dispatch(setLoginPending(false)); + dispatch(setLoginPending(false)) if (!error) { - dispatch(setLoginSuccess(true)); + dispatch(setLoginSuccess(true)) } else { - dispatch(setLoginError(error)); + dispatch(setLoginError(error)) } - }); - } -} - -function setLoginPending(isLoginPending) { - return { - type: SET_LOGIN_PENDING, - isLoginPending - }; -} - -function setLoginSuccess(isLoginSuccess) { - return { - type: SET_LOGIN_SUCCESS, - isLoginSuccess - }; -} - -function setLoginError(loginError) { - return { - type: SET_LOGIN_ERROR, - loginError + }) } } function callLoginApi(email, password, callback) { setTimeout(() => { if (email === 'admin@example.com' && password === 'admin') { - return callback(null); + return callback(null) } else { - return callback(new Error('Invalid email and password')); + return callback(new Error('Invalid email and password')) } - }, 1000); + }, 1000) } -export default function reducer(state = { - isLoginSuccess: false, - isLoginPending: false, - loginError: null -}, action) { +export default function reducer( + state = { + isLoginSuccess: false, + isLoginPending: false, + loginError: null + }, + action +) { switch (action.type) { case SET_LOGIN_PENDING: - return Object.assign({}, state, { - isLoginPending: action.isLoginPending - }); - + return { + ...state, + isLoginPending: action.payload + } case SET_LOGIN_SUCCESS: - return Object.assign({}, state, { - isLoginSuccess: action.isLoginSuccess - }); + return { + ...state, + isLoginSuccess: action.payload + } case SET_LOGIN_ERROR: - return Object.assign({}, state, { - loginError: action.loginError - }); + return { + ...state, + loginError: action.payload + } default: - return state; + return state } -} \ No newline at end of file +} From 10cc0ed169b49af80b506be7811dabc959615ee4 Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 19:59:21 -0300 Subject: [PATCH 08/24] ES6 sintax improvements --- src/redux/reducer.js | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/src/redux/reducer.js b/src/redux/reducer.js index 5a85a40..9335490 100644 --- a/src/redux/reducer.js +++ b/src/redux/reducer.js @@ -8,24 +8,22 @@ const setLoginPending = createAction(SET_LOGIN_PENDING) const setLoginSuccess = createAction(SET_LOGIN_SUCCESS) const setLoginError = createAction(SET_LOGIN_ERROR) -export function login(email, password) { - return dispatch => { - dispatch(setLoginPending(true)) - dispatch(setLoginSuccess(false)) - dispatch(setLoginError(null)) +export const login = (email, password) => dispatch => { + dispatch(setLoginPending(true)) + dispatch(setLoginSuccess(false)) + dispatch(setLoginError(null)) - callLoginApi(email, password, error => { - dispatch(setLoginPending(false)) - if (!error) { - dispatch(setLoginSuccess(true)) - } else { - dispatch(setLoginError(error)) - } - }) - } + callLoginApi(email, password, error => { + dispatch(setLoginPending(false)) + if (!error) { + dispatch(setLoginSuccess(true)) + } else { + dispatch(setLoginError(error)) + } + }) } -function callLoginApi(email, password, callback) { +const callLoginApi = (email, password, callback) => { setTimeout(() => { if (email === 'admin@example.com' && password === 'admin') { return callback(null) @@ -35,14 +33,13 @@ function callLoginApi(email, password, callback) { }, 1000) } -export default function reducer( - state = { - isLoginSuccess: false, - isLoginPending: false, - loginError: null - }, - action -) { +const initalState = { + isLoginSuccess: false, + isLoginPending: false, + loginError: null +} + +export default (state = initalState, action) => { switch (action.type) { case SET_LOGIN_PENDING: return { From 21ed2490fa77eb1e6716173027a5c2c1b54fb8c4 Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 20:11:31 -0300 Subject: [PATCH 09/24] status logic changed to something more simple --- src/components/LoginForm.js | 10 +++------ src/redux/reducer.js | 42 ++++++++++++++++++------------------- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/components/LoginForm.js b/src/components/LoginForm.js index 5111cef..04b8a8a 100644 --- a/src/components/LoginForm.js +++ b/src/components/LoginForm.js @@ -30,7 +30,7 @@ class LoginForm extends Component { render() { let {email, password} = this.state - let {isLoginPending, isLoginSuccess, loginError} = this.props + let {text} = this.props.Login return (
@@ -58,9 +58,7 @@ class LoginForm extends Component {
- {isLoginPending &&
Please wait...
} - {isLoginSuccess &&
Success.
} - {loginError &&
{loginError.message}
} + {text}
) @@ -68,9 +66,7 @@ class LoginForm extends Component { } const mapStateToProps = state => ({ - isLoginPending: state.isLoginPending, - isLoginSuccess: state.isLoginSuccess, - loginError: state.loginError + Login: state }) const mapDispatchToProps = dispatch => ({ diff --git a/src/redux/reducer.js b/src/redux/reducer.js index 9335490..3b9ba29 100644 --- a/src/redux/reducer.js +++ b/src/redux/reducer.js @@ -8,21 +8,6 @@ const setLoginPending = createAction(SET_LOGIN_PENDING) const setLoginSuccess = createAction(SET_LOGIN_SUCCESS) const setLoginError = createAction(SET_LOGIN_ERROR) -export const login = (email, password) => dispatch => { - dispatch(setLoginPending(true)) - dispatch(setLoginSuccess(false)) - dispatch(setLoginError(null)) - - callLoginApi(email, password, error => { - dispatch(setLoginPending(false)) - if (!error) { - dispatch(setLoginSuccess(true)) - } else { - dispatch(setLoginError(error)) - } - }) -} - const callLoginApi = (email, password, callback) => { setTimeout(() => { if (email === 'admin@example.com' && password === 'admin') { @@ -33,10 +18,21 @@ const callLoginApi = (email, password, callback) => { }, 1000) } +export const login = (email, password) => dispatch => { + dispatch(setLoginPending()) + + callLoginApi(email, password, error => { + if (error) { + dispatch(setLoginError(error)) + } + dispatch(setLoginSuccess()) + }) +} + const initalState = { - isLoginSuccess: false, - isLoginPending: false, - loginError: null + requestPending: false, + text: null, + error: false } export default (state = initalState, action) => { @@ -44,18 +40,22 @@ export default (state = initalState, action) => { case SET_LOGIN_PENDING: return { ...state, - isLoginPending: action.payload + text: 'Please wait...', + requestPending: true } case SET_LOGIN_SUCCESS: return { ...state, - isLoginSuccess: action.payload + text: 'Success', + requestPending: false } case SET_LOGIN_ERROR: return { ...state, - loginError: action.payload + requestPending: false, + error: true, + text: action.payload } default: From 4fff2c22c2f53794006567a2447a6d02aee231f2 Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 20:20:20 -0300 Subject: [PATCH 10/24] error logic changed to something more simple --- src/components/LoginForm.js | 3 ++- src/redux/reducer.js | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/LoginForm.js b/src/components/LoginForm.js index 04b8a8a..b9ebe2c 100644 --- a/src/components/LoginForm.js +++ b/src/components/LoginForm.js @@ -30,7 +30,7 @@ class LoginForm extends Component { render() { let {email, password} = this.state - let {text} = this.props.Login + let {text, error} = this.props.Login return (
@@ -59,6 +59,7 @@ class LoginForm extends Component {
{text} + {error && error.message}
) diff --git a/src/redux/reducer.js b/src/redux/reducer.js index 3b9ba29..d96b4ea 100644 --- a/src/redux/reducer.js +++ b/src/redux/reducer.js @@ -23,7 +23,7 @@ export const login = (email, password) => dispatch => { callLoginApi(email, password, error => { if (error) { - dispatch(setLoginError(error)) + return dispatch(setLoginError(error)) } dispatch(setLoginSuccess()) }) @@ -54,8 +54,8 @@ export default (state = initalState, action) => { return { ...state, requestPending: false, - error: true, - text: action.payload + text: null, + error: action.payload } default: From bdbd0168d754f73eb14d0a6a4fc895d93347865c Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 20:24:28 -0300 Subject: [PATCH 11/24] reducer changed to handleActions from redux-actions --- src/redux/reducer.js | 52 ++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/src/redux/reducer.js b/src/redux/reducer.js index d96b4ea..807050d 100644 --- a/src/redux/reducer.js +++ b/src/redux/reducer.js @@ -1,4 +1,4 @@ -import {createAction} from 'redux-actions' +import {createAction, handleActions} from 'redux-actions' const SET_LOGIN_PENDING = 'modules/Login/PENDING' const SET_LOGIN_SUCCESS = 'modules/Login/SUCCESS' @@ -35,30 +35,26 @@ const initalState = { error: false } -export default (state = initalState, action) => { - switch (action.type) { - case SET_LOGIN_PENDING: - return { - ...state, - text: 'Please wait...', - requestPending: true - } - case SET_LOGIN_SUCCESS: - return { - ...state, - text: 'Success', - requestPending: false - } - - case SET_LOGIN_ERROR: - return { - ...state, - requestPending: false, - text: null, - error: action.payload - } - - default: - return state - } -} +const reducer = handleActions( + { + [SET_LOGIN_PENDING]: (state, action) => ({ + ...state, + text: 'Please wait...', + requestPending: true + }), + [SET_LOGIN_SUCCESS]: (state, action) => ({ + ...state, + text: 'Success', + requestPending: false + }), + [SET_LOGIN_ERROR]: (state, action) => ({ + ...state, + requestPending: false, + text: null, + error: action.payload + }) + }, + initalState +) + +export default reducer From 455e0f016918015b5af5132cc0bafc203c093e29 Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 20:34:41 -0300 Subject: [PATCH 12/24] Files and login changes to organize between presentational and container components --- src/components/LoginForm.js | 101 ++++++++++++------------------------ src/containers/Login.js | 57 ++++++++++++++++++++ src/index.js | 2 +- 3 files changed, 90 insertions(+), 70 deletions(-) diff --git a/src/components/LoginForm.js b/src/components/LoginForm.js index b9ebe2c..0a77795 100644 --- a/src/components/LoginForm.js +++ b/src/components/LoginForm.js @@ -1,77 +1,40 @@ -import React, {Component} from 'react' -import {connect} from 'react-redux' -import {login} from '../redux/reducer' +import React from 'react' import '../assets/LoginForm.css' -class LoginForm extends Component { - constructor(props) { - super(props) - this.state = { - email: '', - password: '' - } - } - - handleSubmit = e => { - e.preventDefault() - let {email, password} = this.state - this.props.login(email, password) - this.setState({ - email: '', - password: '' - }) - } - - handleFieldChange = e => { - this.setState({ - [e.target.name]: e.target.value - }) - } - - render() { - let {email, password} = this.state - let {text, error} = this.props.Login - return ( -
-
-
- - -
- -
- - -
+const LoginForm = props => { + const {email, password, text, error, handleSubmit, handleFieldChange} = props + return ( + +
+
+ +
- - -
- {text} - {error && error.message} +
+ +
- - ) - } -} +
-const mapStateToProps = state => ({ - Login: state -}) + -const mapDispatchToProps = dispatch => ({ - login: (email, password) => dispatch(login(email, password)) -}) +
+ {text} + {error && error.message} +
+ + ) +} -export default connect(mapStateToProps, mapDispatchToProps)(LoginForm) +export default LoginForm diff --git a/src/containers/Login.js b/src/containers/Login.js index e69de29..885a0d0 100644 --- a/src/containers/Login.js +++ b/src/containers/Login.js @@ -0,0 +1,57 @@ +import React, {Component} from 'react' +import {connect} from 'react-redux' +import {login} from '../redux/reducer' +import LoginForm from '../components/LoginForm' + +class Login extends Component { + constructor(props) { + super(props) + this.state = { + email: '', + password: '' + } + } + + handleSubmit = e => { + e.preventDefault() + let {email, password} = this.state + this.props.login(email, password) + this.setState({ + email: '', + password: '' + }) + } + + handleFieldChange = e => { + this.setState({ + [e.target.name]: e.target.value + }) + } + + render() { + let {email, password} = this.state + let {text, error} = this.props.Login + return ( +
+ +
+ ) + } +} + +const mapStateToProps = state => ({ + Login: state +}) + +const mapDispatchToProps = dispatch => ({ + login: (email, password) => dispatch(login(email, password)) +}) + +export default connect(mapStateToProps, mapDispatchToProps)(Login) diff --git a/src/index.js b/src/index.js index 3ddd965..c3d1828 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,6 @@ import React from 'react' import ReactDOM from 'react-dom' -import LoginForm from './components/LoginForm' +import LoginForm from './containers/Login' import {Provider} from 'react-redux' import store from './redux/store' From 3e04e4620b05b4c841503e82731e2d51e058d1cc Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 21:01:40 -0300 Subject: [PATCH 13/24] fetch-middleware added and action logic changed --- package.json | 1 + src/containers/Login.js | 14 ++++++------ src/redux/reducer.js | 47 ++++++++++++++++++++--------------------- src/redux/store.js | 12 +++++------ 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 0df195f..0482273 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "fetch-middleware": "^1.0.4", "react": "^15.5.4", "react-dom": "^15.5.4", "react-redux": "^5.0.4", diff --git a/src/containers/Login.js b/src/containers/Login.js index 885a0d0..2fdf647 100644 --- a/src/containers/Login.js +++ b/src/containers/Login.js @@ -1,5 +1,7 @@ import React, {Component} from 'react' import {connect} from 'react-redux' +import {bindActionCreators} from 'redux' + import {login} from '../redux/reducer' import LoginForm from '../components/LoginForm' @@ -14,8 +16,8 @@ class Login extends Component { handleSubmit = e => { e.preventDefault() - let {email, password} = this.state - this.props.login(email, password) + const {email, password} = this.state + this.props.login({email, password}) this.setState({ email: '', password: '' @@ -29,8 +31,8 @@ class Login extends Component { } render() { - let {email, password} = this.state - let {text, error} = this.props.Login + const {email, password} = this.state + const {text, error} = this.props.Login return (
({ Login: state }) -const mapDispatchToProps = dispatch => ({ - login: (email, password) => dispatch(login(email, password)) -}) +const mapDispatchToProps = dispatch => bindActionCreators({login}, dispatch) export default connect(mapStateToProps, mapDispatchToProps)(Login) diff --git a/src/redux/reducer.js b/src/redux/reducer.js index 807050d..ab680b4 100644 --- a/src/redux/reducer.js +++ b/src/redux/reducer.js @@ -4,31 +4,28 @@ const SET_LOGIN_PENDING = 'modules/Login/PENDING' const SET_LOGIN_SUCCESS = 'modules/Login/SUCCESS' const SET_LOGIN_ERROR = 'modules/Login/ERROR' -const setLoginPending = createAction(SET_LOGIN_PENDING) -const setLoginSuccess = createAction(SET_LOGIN_SUCCESS) -const setLoginError = createAction(SET_LOGIN_ERROR) - -const callLoginApi = (email, password, callback) => { - setTimeout(() => { - if (email === 'admin@example.com' && password === 'admin') { - return callback(null) - } else { - return callback(new Error('Invalid email and password')) - } - }, 1000) -} - -export const login = (email, password) => dispatch => { - dispatch(setLoginPending()) - - callLoginApi(email, password, error => { - if (error) { - return dispatch(setLoginError(error)) - } - dispatch(setLoginSuccess()) +const loginPending = createAction(SET_LOGIN_PENDING) +const loginSuccess = createAction(SET_LOGIN_SUCCESS) +const loginError = createAction(SET_LOGIN_ERROR) + +const callLoginApi = values => { + return new Promise((resolve, reject) => { + setTimeout(() => { + if (values.email !== 'admin@example.com' || values.password !== 'admin') { + return reject(new Error('Invalid email and password')) + } + return resolve(true) + }, 1000) }) } +export const login = values => ({ + type: [loginPending, loginSuccess, loginError], + payload: { + data: () => callLoginApi(values) + } +}) + const initalState = { requestPending: false, text: null, @@ -39,13 +36,15 @@ const reducer = handleActions( { [SET_LOGIN_PENDING]: (state, action) => ({ ...state, + requestPending: true, text: 'Please wait...', - requestPending: true + error: false }), [SET_LOGIN_SUCCESS]: (state, action) => ({ ...state, + requestPending: false, text: 'Success', - requestPending: false + error: false }), [SET_LOGIN_ERROR]: (state, action) => ({ ...state, diff --git a/src/redux/store.js b/src/redux/store.js index b195a9a..1a77045 100644 --- a/src/redux/store.js +++ b/src/redux/store.js @@ -1,7 +1,7 @@ -import { createStore, applyMiddleware } from 'redux'; -import thunk from 'redux-thunk'; -import logger from 'redux-logger'; -import reducer from './reducer'; +import {createStore, applyMiddleware} from 'redux' +import fetchMiddleware from 'fetch-middleware' +import logger from 'redux-logger' +import Login from './reducer' -const store = createStore(reducer, {}, applyMiddleware(thunk, logger)); -export default store; \ No newline at end of file +const store = createStore(Login, {}, applyMiddleware(fetchMiddleware, logger)) +export default store From d18b8695fc65dd09fa2236a99774b219d5975cd7 Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 21:02:13 -0300 Subject: [PATCH 14/24] redux-thunk removed --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 0482273..e3e31d2 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,7 @@ "react-redux": "^5.0.4", "redux": "^3.6.0", "redux-actions": "^2.0.2", - "redux-logger": "^3.0.1", - "redux-thunk": "^2.2.0" + "redux-logger": "^3.0.1" }, "devDependencies": { "react-scripts": "0.9.5" From b8fd390f75c071031f2ddb534e46208c34644a09 Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 21:04:54 -0300 Subject: [PATCH 15/24] reducer and actions divided in two files --- src/redux/actions.js | 27 +++++++++++++++++++++++++++ src/redux/reducer.js | 26 +------------------------- 2 files changed, 28 insertions(+), 25 deletions(-) create mode 100644 src/redux/actions.js diff --git a/src/redux/actions.js b/src/redux/actions.js new file mode 100644 index 0000000..dfce2b4 --- /dev/null +++ b/src/redux/actions.js @@ -0,0 +1,27 @@ +import {createAction} from 'redux-actions' + +export const SET_LOGIN_PENDING = 'modules/Login/PENDING' +export const SET_LOGIN_SUCCESS = 'modules/Login/SUCCESS' +export const SET_LOGIN_ERROR = 'modules/Login/ERROR' + +const loginPending = createAction(SET_LOGIN_PENDING) +const loginSuccess = createAction(SET_LOGIN_SUCCESS) +const loginError = createAction(SET_LOGIN_ERROR) + +const fakeLoginApi = values => { + return new Promise((resolve, reject) => { + setTimeout(() => { + if (values.email !== 'admin@example.com' || values.password !== 'admin') { + return reject(new Error('Invalid email and password')) + } + return resolve(true) + }, 1000) + }) +} + +export const login = values => ({ + type: [loginPending, loginSuccess, loginError], + payload: { + data: () => fakeLoginApi(values) + } +}) diff --git a/src/redux/reducer.js b/src/redux/reducer.js index ab680b4..e7b3837 100644 --- a/src/redux/reducer.js +++ b/src/redux/reducer.js @@ -1,30 +1,6 @@ import {createAction, handleActions} from 'redux-actions' -const SET_LOGIN_PENDING = 'modules/Login/PENDING' -const SET_LOGIN_SUCCESS = 'modules/Login/SUCCESS' -const SET_LOGIN_ERROR = 'modules/Login/ERROR' - -const loginPending = createAction(SET_LOGIN_PENDING) -const loginSuccess = createAction(SET_LOGIN_SUCCESS) -const loginError = createAction(SET_LOGIN_ERROR) - -const callLoginApi = values => { - return new Promise((resolve, reject) => { - setTimeout(() => { - if (values.email !== 'admin@example.com' || values.password !== 'admin') { - return reject(new Error('Invalid email and password')) - } - return resolve(true) - }, 1000) - }) -} - -export const login = values => ({ - type: [loginPending, loginSuccess, loginError], - payload: { - data: () => callLoginApi(values) - } -}) +import {SET_LOGIN_PENDING, SET_LOGIN_SUCCESS, SET_LOGIN_ERROR} from './actions' const initalState = { requestPending: false, From 6e179b9b32d2501c4318d1bb05dbfa9fc2ea125e Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 21:06:02 -0300 Subject: [PATCH 16/24] readme changes --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c358b49..435a79d 100644 --- a/README.md +++ b/README.md @@ -12,3 +12,6 @@ npm intall npm start ``` +# Example +**Login:** admin@example.com +**Password:** admin From 4c23a8c2c7163694ac689f85f93bb5446bc8a9e0 Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 21:06:40 -0300 Subject: [PATCH 17/24] createAction removed from reducer --- src/redux/reducer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/redux/reducer.js b/src/redux/reducer.js index e7b3837..6ab3588 100644 --- a/src/redux/reducer.js +++ b/src/redux/reducer.js @@ -1,4 +1,4 @@ -import {createAction, handleActions} from 'redux-actions' +import {handleActions} from 'redux-actions' import {SET_LOGIN_PENDING, SET_LOGIN_SUCCESS, SET_LOGIN_ERROR} from './actions' From 11f503412180ff902dba32f1fec91f15343c06d5 Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 21:08:10 -0300 Subject: [PATCH 18/24] readme changes --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 435a79d..e40162d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # React Redux Login Flow A simple application to demonstrate a login flow with React & Redux. -Read full tutorial at here: ![A simple login flow with React and Redux](http://jslancer.com/?p=1213&preview=true) +Read full tutorial at here: [A simple login flow with React and Redux](http://jslancer.com/?p=1213&preview=true) ![ezgif com-video-to-gif](https://cloud.githubusercontent.com/assets/1154740/25493010/fed3b2cc-2b9e-11e7-8736-9250549128ec.gif) @@ -13,5 +13,5 @@ npm start ``` # Example -**Login:** admin@example.com -**Password:** admin +Login: admin@example.com +Password: admin From 7e9970ae9f6290571e4ec66b8151fdcc92ee0339 Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 22:00:25 -0300 Subject: [PATCH 19/24] just render method is used from react-dom --- src/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index c3d1828..7a48fcb 100644 --- a/src/index.js +++ b/src/index.js @@ -1,10 +1,11 @@ import React from 'react' -import ReactDOM from 'react-dom' -import LoginForm from './containers/Login' +import {render} from 'react-dom' import {Provider} from 'react-redux' + +import LoginForm from './containers/Login' import store from './redux/store' -ReactDOM.render( +render( , From 1acab40758a9356170183906c8814947126a7d4f Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Thu, 27 Apr 2017 22:04:56 -0300 Subject: [PATCH 20/24] redux devtools added, reference to login action fixed --- src/containers/Login.js | 2 +- src/index.js | 2 +- src/redux/store.js | 11 ++++++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/containers/Login.js b/src/containers/Login.js index 2fdf647..fc57443 100644 --- a/src/containers/Login.js +++ b/src/containers/Login.js @@ -2,7 +2,7 @@ import React, {Component} from 'react' import {connect} from 'react-redux' import {bindActionCreators} from 'redux' -import {login} from '../redux/reducer' +import {login} from '../redux/actions' import LoginForm from '../components/LoginForm' class Login extends Component { diff --git a/src/index.js b/src/index.js index 7a48fcb..8adea6e 100644 --- a/src/index.js +++ b/src/index.js @@ -3,7 +3,7 @@ import {render} from 'react-dom' import {Provider} from 'react-redux' import LoginForm from './containers/Login' -import store from './redux/store' +import {store} from './redux/store' render( diff --git a/src/redux/store.js b/src/redux/store.js index 1a77045..a6e5258 100644 --- a/src/redux/store.js +++ b/src/redux/store.js @@ -1,7 +1,12 @@ -import {createStore, applyMiddleware} from 'redux' +import {createStore, applyMiddleware, compose} from 'redux' import fetchMiddleware from 'fetch-middleware' import logger from 'redux-logger' import Login from './reducer' -const store = createStore(Login, {}, applyMiddleware(fetchMiddleware, logger)) -export default store +const devTools = window.devToolsExtension ? window.devToolsExtension() : f => f + +export const store = createStore( + Login, + {}, + compose(applyMiddleware(fetchMiddleware, logger), devTools) +) From 552d1cdf4c31ec709170645b8f4f1a0b05727859 Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Fri, 28 Apr 2017 08:41:51 -0300 Subject: [PATCH 21/24] version update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e3e31d2..af177a1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-redux-login-flow", - "version": "0.1.0", + "version": "0.2.0", "private": true, "dependencies": { "fetch-middleware": "^1.0.4", From d75b64ea427368529d8255a97df75da968958f43 Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Fri, 28 Apr 2017 08:42:38 -0300 Subject: [PATCH 22/24] just changed props order to organize --- src/containers/Login.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/Login.js b/src/containers/Login.js index fc57443..0285de5 100644 --- a/src/containers/Login.js +++ b/src/containers/Login.js @@ -37,9 +37,9 @@ class Login extends Component {
From 172acf3bec6e8ac92f7759207c5fd8b9f73efd23 Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Fri, 28 Apr 2017 08:45:09 -0300 Subject: [PATCH 23/24] tutorial link updated --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e40162d..9d220b4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # React Redux Login Flow A simple application to demonstrate a login flow with React & Redux. -Read full tutorial at here: [A simple login flow with React and Redux](http://jslancer.com/?p=1213&preview=true) +Read full tutorial at here: [A simple login flow with React and Redux](http://jslancer.com/2017/04/27/a-simple-login-flow-with-react-and-redux) ![ezgif com-video-to-gif](https://cloud.githubusercontent.com/assets/1154740/25493010/fed3b2cc-2b9e-11e7-8736-9250549128ec.gif) From 1d9ffb07e14de3ca0cd27add637aa54971082c7f Mon Sep 17 00:00:00 2001 From: Junior Oliveira Date: Fri, 28 Apr 2017 14:21:42 -0300 Subject: [PATCH 24/24] refactoring to functional setState --- src/containers/Login.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/containers/Login.js b/src/containers/Login.js index 0285de5..265d269 100644 --- a/src/containers/Login.js +++ b/src/containers/Login.js @@ -5,23 +5,22 @@ import {bindActionCreators} from 'redux' import {login} from '../redux/actions' import LoginForm from '../components/LoginForm' +const emptyForm = () => ({ + email: '', + password: '' +}) + class Login extends Component { constructor(props) { super(props) - this.state = { - email: '', - password: '' - } + this.state = emptyForm() } handleSubmit = e => { e.preventDefault() const {email, password} = this.state this.props.login({email, password}) - this.setState({ - email: '', - password: '' - }) + this.setState(emptyForm) } handleFieldChange = e => {