Skip to content

Instantly share code, notes, and snippets.

@JasperVercammen
Created August 29, 2017 07:38
Show Gist options
  • Save JasperVercammen/d832e1c6d32bf3dc816f8c4b813e62bc to your computer and use it in GitHub Desktop.
Save JasperVercammen/d832e1c6d32bf3dc816f8c4b813e62bc to your computer and use it in GitHub Desktop.
Async form handling
import constants from './constants';
import asyncActionHandler from '../asyncActionHandler';
export const loginUser = asyncActionHandler(constants.LOGIN_USER, (email, password = '') =>
fetch('https://reqres.in/api/login', {
method: 'POST',
body: JSON.stringify({
email,
password,
}),
headers: {
accept: 'application/json',
},
})
.then((response) => response.json())
.then((response) => {
if (response.error) {
throw new Error(response.error);
} else {
return response;
}
})
);
export const logoutUser = () => {
return {
type: constants.LOGOUT_USER,
};
};
const makeActionCreator = (type, argument) => {
if (!type) {
throw new Error('Type required! You can\'t have an action without a type.');
}
return (args) => {
return ({
type,
[argument]: args
});
};
};
const asyncActionHandler = (actionName, asyncAction) => {
const name = actionName.toUpperCase();
const requestAction = makeActionCreator(`${name}_REQUEST`, 'params');
const loadedAction = makeActionCreator(`${name}_LOADED`, 'payload');
const errorAction = makeActionCreator(`${name}_FAILED`, 'error');
return (...args) => {
return async (dispatch) => {
dispatch(requestAction(args));
try {
const data = await asyncAction(...args);
dispatch(loadedAction(data));
} catch (error) {
dispatch(errorAction(error));
}
};
};
};
export default asyncActionHandler;
import {connect} from 'react-redux';
import {loginUser} from '../../modules/user/actions';
import LoginView from './Login.view';
const mapStateToProps = (state) => ({
isLoggedIn: state.user.isLoggedIn,
error: state.user.error,
errorMsg: state.user.errorMsg,
});
const mapDispatchToProps = {
loginUser,
};
const LoginPage = connect(mapStateToProps, mapDispatchToProps)(LoginView);
export {LoginPage};
import React, {Component} from 'react';
import {Button} from '../../components';
import './Login.style.css';
class LoginView extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
};
}
onLoginUser = async () => {
await this.props.loginUser(this.state.email, this.state.password);
};
onChange = (event) => {
this.setState({
[event.target.name]: event.target.value,
});
};
render() {
return (
<div className='login-view'>
<h1 className='title'>Login</h1>
<label htmlFor='email'>Email</label>
<input type='text' name='email' placeholder='Your email' id='email' onChange={this.onChange} />
<label htmlFor='password'>Password</label>
<input type='password' name='password' placeholder='Your password' id='password' onChange={this.onChange} />
{this.props.error && <span className='error'>{this.props.errorMsg}</span>}
<div className='actions'>
<Button onClickCallback={this.onLoginUser}>login</Button>
</div>
</div>
);
}
}
export default LoginView;
import constants from './constants';
const initialState = {
username: '',
email: '',
isLoggedIn: false,
isLoading: false,
error: false,
errorMsg: '',
};
const user = (state = initialState, action = {}) => {
const payload = action.payload;
const error = action.error;
switch (action.type) {
case constants.LOGIN_USER_REQUESTED:
return {
...state,
isLoading: true,
};
case constants.LOGIN_USER_LOADED:
return {
...state,
isLoading: false,
isLoggedIn: true,
error: false,
errorMsg: '',
};
case constants.LOGIN_USER_FAILED:
return {
...state,
isLoggedIn: false,
isLoading: false,
error: true,
errorMsg: error.message,
};
case constants.LOGOUT_USER:
return initialState;
default:
return state;
}
};
export default user;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment