diff --git a/README.md b/README.md
index c358b49..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: 
+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)

@@ -12,3 +12,6 @@ npm intall
npm start
```
+# Example
+Login: admin@example.com
+Password: admin
diff --git a/package.json b/package.json
index 78afb16..af177a1 100644
--- a/package.json
+++ b/package.json
@@ -1,14 +1,15 @@
{
"name": "react-redux-login-flow",
- "version": "0.1.0",
+ "version": "0.2.0",
"private": true,
"dependencies": {
+ "fetch-middleware": "^1.0.4",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-redux": "^5.0.4",
"redux": "^3.6.0",
- "redux-logger": "^3.0.1",
- "redux-thunk": "^2.2.0"
+ "redux-actions": "^2.0.2",
+ "redux-logger": "^3.0.1"
},
"devDependencies": {
"react-scripts": "0.9.5"
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..0a77795
--- /dev/null
+++ b/src/components/LoginForm.js
@@ -0,0 +1,40 @@
+import React from 'react'
+import '../assets/LoginForm.css'
+
+const LoginForm = props => {
+ const {email, password, text, error, handleSubmit, handleFieldChange} = props
+ return (
+
+ )
+}
+
+export default 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 (
-
- )
- }
-
- 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..265d269
--- /dev/null
+++ b/src/containers/Login.js
@@ -0,0 +1,56 @@
+import React, {Component} from 'react'
+import {connect} from 'react-redux'
+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 = emptyForm()
+ }
+
+ handleSubmit = e => {
+ e.preventDefault()
+ const {email, password} = this.state
+ this.props.login({email, password})
+ this.setState(emptyForm)
+ }
+
+ handleFieldChange = e => {
+ this.setState({
+ [e.target.name]: e.target.value
+ })
+ }
+
+ render() {
+ const {email, password} = this.state
+ const {text, error} = this.props.Login
+ return (
+
+
+
+ )
+ }
+}
+
+const mapStateToProps = state => ({
+ Login: state
+})
+
+const mapDispatchToProps = dispatch => bindActionCreators({login}, dispatch)
+
+export default connect(mapStateToProps, mapDispatchToProps)(Login)
diff --git a/src/index.js b/src/index.js
index 813f834..8adea6e 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,12 +1,13 @@
-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 {render} from 'react-dom'
+import {Provider} from 'react-redux'
-ReactDOM.render(
+import LoginForm from './containers/Login'
+import {store} from './redux/store'
+
+render(
,
document.getElementById('root')
-);
+)
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 d958b37..6ab3588 100644
--- a/src/redux/reducer.js
+++ b/src/redux/reducer.js
@@ -1,77 +1,35 @@
-const SET_LOGIN_PENDING = 'SET_LOGIN_PENDING';
-const SET_LOGIN_SUCCESS = 'SET_LOGIN_SUCCESS';
-const SET_LOGIN_ERROR = 'SET_LOGIN_ERROR';
+import {handleActions} from 'redux-actions'
-export function login(email, password) {
- return dispatch => {
- dispatch(setLoginPending(true));
- dispatch(setLoginSuccess(false));
- dispatch(setLoginError(null));
+import {SET_LOGIN_PENDING, SET_LOGIN_SUCCESS, SET_LOGIN_ERROR} from './actions'
- callLoginApi(email, password, error => {
- dispatch(setLoginPending(false));
- if (!error) {
- dispatch(setLoginSuccess(true));
- } else {
- dispatch(setLoginError(error));
- }
- });
- }
+const initalState = {
+ requestPending: false,
+ text: null,
+ error: false
}
-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);
- } else {
- return callback(new Error('Invalid email and password'));
- }
- }, 1000);
-}
-
-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
- });
-
- case SET_LOGIN_SUCCESS:
- return Object.assign({}, state, {
- isLoginSuccess: action.isLoginSuccess
- });
-
- case SET_LOGIN_ERROR:
- return Object.assign({}, state, {
- loginError: action.loginError
- });
-
- default:
- return state;
- }
-}
\ No newline at end of file
+const reducer = handleActions(
+ {
+ [SET_LOGIN_PENDING]: (state, action) => ({
+ ...state,
+ requestPending: true,
+ text: 'Please wait...',
+ error: false
+ }),
+ [SET_LOGIN_SUCCESS]: (state, action) => ({
+ ...state,
+ requestPending: false,
+ text: 'Success',
+ error: false
+ }),
+ [SET_LOGIN_ERROR]: (state, action) => ({
+ ...state,
+ requestPending: false,
+ text: null,
+ error: action.payload
+ })
+ },
+ initalState
+)
+
+export default reducer
diff --git a/src/redux/store.js b/src/redux/store.js
index b195a9a..a6e5258 100644
--- a/src/redux/store.js
+++ b/src/redux/store.js
@@ -1,7 +1,12 @@
-import { createStore, applyMiddleware } from 'redux';
-import thunk from 'redux-thunk';
-import logger from 'redux-logger';
-import reducer from './reducer';
+import {createStore, applyMiddleware, compose} 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 devTools = window.devToolsExtension ? window.devToolsExtension() : f => f
+
+export const store = createStore(
+ Login,
+ {},
+ compose(applyMiddleware(fetchMiddleware, logger), devTools)
+)