Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
160a747
Folder structure changed
arojunior Apr 27, 2017
54ac45c
method onSubmit changed to handleSubmit (its a convention)
arojunior Apr 27, 2017
ed1644a
handleSubmit now its an arrow function and its not needed to bind
arojunior Apr 27, 2017
f6177c8
method handleFieldChange added, its not good change state from render…
arojunior Apr 27, 2017
24b0a88
ES6 sintax improvements
arojunior Apr 27, 2017
be4c671
redux-actions added
arojunior Apr 27, 2017
bccaa5e
refactoring to redux-actions
arojunior Apr 27, 2017
10cc0ed
ES6 sintax improvements
arojunior Apr 27, 2017
21ed249
status logic changed to something more simple
arojunior Apr 27, 2017
4fff2c2
error logic changed to something more simple
arojunior Apr 27, 2017
bdbd016
reducer changed to handleActions from redux-actions
arojunior Apr 27, 2017
455e0f0
Files and login changes to organize between presentational and contai…
arojunior Apr 27, 2017
3e04e46
fetch-middleware added and action logic changed
arojunior Apr 28, 2017
d18b869
redux-thunk removed
arojunior Apr 28, 2017
b8fd390
reducer and actions divided in two files
arojunior Apr 28, 2017
6e179b9
readme changes
arojunior Apr 28, 2017
4c23a8c
createAction removed from reducer
arojunior Apr 28, 2017
11f5034
readme changes
arojunior Apr 28, 2017
7e9970a
just render method is used from react-dom
arojunior Apr 28, 2017
1acab40
redux devtools added, reference to login action fixed
arojunior Apr 28, 2017
552d1cd
version update
arojunior Apr 28, 2017
d75b64e
just changed props order to organize
arojunior Apr 28, 2017
172acf3
tutorial link updated
arojunior Apr 28, 2017
1d9ffb0
refactoring to functional setState
arojunior Apr 28, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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)

Expand All @@ -12,3 +12,6 @@ npm intall
npm start
```

# Example
Login: admin@example.com
Password: admin
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
File renamed without changes.
40 changes: 40 additions & 0 deletions src/components/LoginForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react'
import '../assets/LoginForm.css'

const LoginForm = props => {
const {email, password, text, error, handleSubmit, handleFieldChange} = props
return (
<form name="loginForm" onSubmit={handleSubmit}>
<div className="form-group-collection">
<div className="form-group">
<label>Email:</label>
<input
type="email"
name="email"
onChange={handleFieldChange}
value={email}
/>
</div>

<div className="form-group">
<label>Password:</label>
<input
type="password"
name="password"
onChange={handleFieldChange}
value={password}
/>
</div>
</div>

<input type="submit" value="Login" />

<div className="message">
{text}
{error && error.message}
</div>
</form>
)
}

export default LoginForm
67 changes: 0 additions & 67 deletions src/components/LoginForm/LoginForm.js

This file was deleted.

56 changes: 56 additions & 0 deletions src/containers/Login.js
Original file line number Diff line number Diff line change
@@ -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 (
<div>
<LoginForm
handleFieldChange={this.handleFieldChange}
handleSubmit={this.handleSubmit}
password={password}
email={email}
text={text}
error={error}
/>
</div>
)
}
}

const mapStateToProps = state => ({
Login: state
})

const mapDispatchToProps = dispatch => bindActionCreators({login}, dispatch)

export default connect(mapStateToProps, mapDispatchToProps)(Login)
15 changes: 8 additions & 7 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -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(
<Provider store={store}>
<LoginForm />
</Provider>,
document.getElementById('root')
);
)
27 changes: 27 additions & 0 deletions src/redux/actions.js
Original file line number Diff line number Diff line change
@@ -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)
}
})
104 changes: 31 additions & 73 deletions src/redux/reducer.js
Original file line number Diff line number Diff line change
@@ -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;
}
}
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
17 changes: 11 additions & 6 deletions src/redux/store.js
Original file line number Diff line number Diff line change
@@ -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;
const devTools = window.devToolsExtension ? window.devToolsExtension() : f => f

export const store = createStore(
Login,
{},
compose(applyMiddleware(fetchMiddleware, logger), devTools)
)