Archived
UserRegister: Improve error handling
- Show field errors in line - Simplify redux usage
This commit is contained in:
1 parent
34bc0a3e30
commit
9285cdf953
8 files changed
+100
-53
No files matched your search
@@ -11,6 +11,7 @@ export function requestLogin(username, password) {
|
||||
return (dispatch) => {
|
||||
// We are now logging in
|
||||
dispatch(loginRequest());
|
||||
|
||||
return fetch(`${Config.DUSKEN_API_URL}/auth/obtain-token/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
||||
@@ -2,16 +2,15 @@ import React, {Component} from 'react';
|
||||
import {connect} from "react-redux";
|
||||
|
||||
import Membership from "./Membership";
|
||||
import ProofContainer from "./ProofContainer";
|
||||
import { logout } from "../../actions";
|
||||
import {requestUserData} from "../../api";
|
||||
import ProofContainer from "./ProofContainer";
|
||||
|
||||
class MembershipContainer extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isAuthenticated: props.isAuthenticated,
|
||||
isFetchingUserData: props.isFetchingUserData,
|
||||
userToken: props.userToken,
|
||||
lastOrder: props.lastOrder,
|
||||
@@ -19,9 +18,6 @@ class MembershipContainer extends Component {
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if(this.state.isAuthenticated !== nextProps.isAuthenticated) {
|
||||
this.setState({isAuthenticated: nextProps.isAuthenticated})
|
||||
}
|
||||
if(this.state.userToken !== nextProps.userToken) {
|
||||
this.setState({userToken: nextProps.userToken}, () => {
|
||||
if(nextProps.userToken) {
|
||||
@@ -60,6 +56,7 @@ class MembershipContainer extends Component {
|
||||
if (this.props.user) {
|
||||
return <ProofContainer
|
||||
user={this.props.user}
|
||||
isAuthenticated={this.props.isAuthenticated}
|
||||
userToken={this.state.userToken}
|
||||
fetchUser={this.fetchUser}
|
||||
onLogoutPress={this.onLogoutPress}
|
||||
@@ -69,17 +66,17 @@ class MembershipContainer extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
function stateToProps(state) {
|
||||
function storeToProps(store) {
|
||||
return {
|
||||
isAuthenticated: state.isAuthenticated,
|
||||
isFetchingUserData: state.isFetchingUserData,
|
||||
userToken: state.userToken,
|
||||
user: state.user,
|
||||
lastOrder: state.lastOrder
|
||||
isAuthenticated: store.isAuthenticated,
|
||||
isFetchingUserData: store.isFetchingUserData,
|
||||
userToken: store.userToken,
|
||||
user: store.user,
|
||||
lastOrder: store.lastOrder
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
stateToProps,
|
||||
storeToProps,
|
||||
{ requestUserData, logout }
|
||||
)(MembershipContainer);
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, {Component} from 'react';
|
||||
import {Container, Header, Content, Form, Item, Input, Label, Spinner, Button, Text, Icon, Toast} from 'native-base';
|
||||
import {Platform, StyleSheet, View} from "react-native";
|
||||
import EmailValidator from 'email-validator';
|
||||
import platform from "native-base/src/theme/variables/platform";
|
||||
|
||||
export default class UserRegister extends Component {
|
||||
constructor(props) {
|
||||
@@ -27,7 +29,9 @@ export default class UserRegister extends Component {
|
||||
for(let key of fieldNames) {
|
||||
const value = this.state[key];
|
||||
if(value === '') {
|
||||
errors[key] = 'Feltet kan ikke være tomt';
|
||||
errors[key] = 'kan ikke være tomt';
|
||||
} else if(key === 'email' && !EmailValidator.validate(value)) {
|
||||
errors[key] = 'er ikke en gyldig e-post';
|
||||
} else {
|
||||
delete errors[key];
|
||||
}
|
||||
@@ -50,12 +54,13 @@ export default class UserRegister extends Component {
|
||||
<Input
|
||||
autoFocus={true}
|
||||
returnKeyType="next"
|
||||
onChangeText={(text) => {
|
||||
this.setState({firstName: text}, () => { this.validateForm('firstName'); });
|
||||
onChangeText={(firstName) => {
|
||||
this.setState({firstName}, () => { this.validateForm('firstName'); });
|
||||
}}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.lastNameInput._root.focus()
|
||||
}}
|
||||
value={this.state.firstName}
|
||||
/>
|
||||
{this.fieldHasError('firstName') && <Icon name='close-circle' />}
|
||||
</Item>;
|
||||
@@ -67,12 +72,13 @@ export default class UserRegister extends Component {
|
||||
<Input
|
||||
ref="lastNameInput"
|
||||
returnKeyType="next"
|
||||
onChangeText={(text) => {
|
||||
this.setState({lastName: text}, () => { this.validateForm('lastName'); });
|
||||
onChangeText={(lastName) => {
|
||||
this.setState({lastName}, () => { this.validateForm('lastName'); });
|
||||
}}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.emailInput._root.focus()
|
||||
}}
|
||||
value={this.state.lastName}
|
||||
/>
|
||||
{this.fieldHasError('lastName') && <Icon name='close-circle' />}
|
||||
</Item>;
|
||||
@@ -87,12 +93,13 @@ export default class UserRegister extends Component {
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
returnKeyType="next"
|
||||
onChangeText={(text) => {
|
||||
this.setState({email: text}, () => { this.validateForm('email'); });
|
||||
onChangeText={(email) => {
|
||||
this.setState({email}, () => { this.validateForm('email'); });
|
||||
}}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.phoneInput._root.focus()
|
||||
}}
|
||||
value={this.state.email}
|
||||
/>
|
||||
{this.fieldHasError('email') && <Icon name='close-circle' />}
|
||||
</Item>;
|
||||
@@ -105,12 +112,13 @@ export default class UserRegister extends Component {
|
||||
ref="phoneInput"
|
||||
returnKeyType="next"
|
||||
keyboardType="phone-pad"
|
||||
onChangeText={(text) => {
|
||||
this.setState({phoneNumber: text}, () => { this.validateForm('phoneNumber'); });
|
||||
onChangeText={(phoneNumber) => {
|
||||
this.setState({phoneNumber}, () => { this.validateForm('phoneNumber'); });
|
||||
}}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.passwordInput._root.focus()
|
||||
}}
|
||||
value={this.state.phoneNumber}
|
||||
/>
|
||||
{this.fieldHasError('phoneNumber') && <Icon name='close-circle' />}
|
||||
</Item>;
|
||||
@@ -124,26 +132,38 @@ export default class UserRegister extends Component {
|
||||
secureTextEntry={true}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
onChangeText={(text) => {
|
||||
this.setState({password: text}, () => { this.validateForm('password'); });
|
||||
onChangeText={(password) => {
|
||||
this.setState({password}, () => { this.validateForm('password'); });
|
||||
}}
|
||||
onSubmitEditing={this.onRegisterPress}
|
||||
value={this.state.password}
|
||||
/>
|
||||
{this.fieldHasError('password') && <Icon name='close-circle' />}
|
||||
</Item>
|
||||
}
|
||||
|
||||
showError = () => {
|
||||
// TODO: Move these from prop to state in componentWillRecieveProps
|
||||
// TODO: Format these and highlight error fields
|
||||
if (this.props.registerError && Object.keys(this.props.registerError).length !== 0) {
|
||||
const err = this.props.registerError.non_field_errors;
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if(this.props.registerError !== nextProps.registerError) {
|
||||
this.setState({'errors': nextProps.registerError})
|
||||
}
|
||||
}
|
||||
|
||||
showNonFieldError = () => {
|
||||
const errors = this.props.registerError;
|
||||
if (Object.keys(errors).length !== 0 && 'non_field_errors' in errors) {
|
||||
const err = errors.non_field_errors;
|
||||
let errorFormatted = err ? err[0] : 'Kunne ikke registrere bruker, prøv igjen...';
|
||||
return <View style={styles.errorBox}><Text style={styles.errorMessage}>{errorFormatted}</Text></View>
|
||||
}
|
||||
else {
|
||||
return <View style={styles.errorBox}/>
|
||||
|
||||
return <View style={styles.errorBox}/>
|
||||
};
|
||||
|
||||
showFieldError = (field) => {
|
||||
if(!this.fieldHasError(field) ) {
|
||||
return;
|
||||
}
|
||||
return <Item style={styles.errorText}><Text style={styles.errorMessage}>{this.state.errors[field]}</Text></Item>;
|
||||
};
|
||||
|
||||
showSpinner = () => {
|
||||
@@ -158,11 +178,16 @@ export default class UserRegister extends Component {
|
||||
<Content keyboardShouldPersistTaps='always'>
|
||||
<Form style={styles.card}>
|
||||
{this.firstNameInput()}
|
||||
{this.showFieldError('firstName')}
|
||||
{this.lastNameInput()}
|
||||
{this.showFieldError('lastName')}
|
||||
{this.emailInput()}
|
||||
{this.showFieldError('email')}
|
||||
{this.phoneNumberInput()}
|
||||
{this.showFieldError('phoneNumber')}
|
||||
{this.passwordInput()}
|
||||
{this.showError()}
|
||||
{this.showFieldError('password')}
|
||||
{this.showNonFieldError()}
|
||||
{this.registerbutton()}
|
||||
{this.showSpinner()}
|
||||
</Form>
|
||||
@@ -172,7 +197,7 @@ export default class UserRegister extends Component {
|
||||
}
|
||||
|
||||
onRegisterPress = () => {
|
||||
if( !this.validateForm() ) {
|
||||
if( !this.canSubmitForm() ) {
|
||||
Toast.show({
|
||||
text: 'Noen av feltene er ikke fylt ut riktig',
|
||||
position: 'bottom',
|
||||
@@ -192,7 +217,7 @@ export default class UserRegister extends Component {
|
||||
};
|
||||
|
||||
registerbutton() {
|
||||
const isDisabled = this.props.isRegisteringUser || !this.canSubmitForm();
|
||||
const isDisabled = this.state.touched.size === 0 || this.props.isRegisteringUser || !this.canSubmitForm();
|
||||
|
||||
return (<Button
|
||||
full
|
||||
@@ -222,7 +247,8 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
errorMessage: {
|
||||
textAlign: 'center',
|
||||
color: 'red',
|
||||
color: platform.btnDangerBg,
|
||||
fontSize: 14,
|
||||
},
|
||||
card: {
|
||||
borderColor: '#e1e8ee',
|
||||
@@ -242,4 +268,8 @@ const styles = StyleSheet.create({
|
||||
margin: 8,
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
errorText: {
|
||||
paddingTop: 2,
|
||||
paddingBottom: 12,
|
||||
}
|
||||
});
|
||||
@@ -9,8 +9,6 @@ class UserRegisterContainer extends Component {
|
||||
super(state);
|
||||
this.state = {
|
||||
isAuthenticated: false,
|
||||
isRegisteringUser: false,
|
||||
registerError: null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +18,6 @@ class UserRegisterContainer extends Component {
|
||||
this.props.onRegister();
|
||||
}
|
||||
}
|
||||
if (this.state.isRegisteringUser !== nextProps.isRegisteringUser) {
|
||||
this.setState({isRegisteringUser: nextProps.isRegisteringUser});
|
||||
}
|
||||
if (this.state.registerError !== nextProps.registerError) {
|
||||
this.setState({registerError: nextProps.registerError});
|
||||
}
|
||||
}
|
||||
|
||||
onRegisterPress = (firstName, lastName, email, phoneNumber, password) => {
|
||||
@@ -33,18 +25,24 @@ class UserRegisterContainer extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
return <UserRegister {...this.state} onRegisterPress={this.onRegisterPress} />;
|
||||
return <UserRegister
|
||||
{...this.state}
|
||||
onRegisterPress={this.onRegisterPress}
|
||||
registerError={this.props.registerError}
|
||||
isRegisteringUser={this.props.isRegisteringUser}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
function stateToProps(state) {
|
||||
function storeToProps(store) {
|
||||
const errors = store.registerError === null ? {} : store.registerError;
|
||||
return {
|
||||
isAuthenticated: state.isAuthenticated,
|
||||
isRegisteringUser: state.isRegisteringUser,
|
||||
registerError: state.registerError
|
||||
isAuthenticated: store.isAuthenticated,
|
||||
isRegisteringUser: store.isRegisteringUser,
|
||||
registerError: errors
|
||||
};
|
||||
}
|
||||
export default connect(
|
||||
stateToProps,
|
||||
storeToProps,
|
||||
{requestRegisterUser}
|
||||
)(UserRegisterContainer);
|
||||
+14
-1
@@ -11,6 +11,8 @@ import {
|
||||
REGISTER_USER_SUCCESS, MEMBERSHIP_CHARGE_REQUEST, MEMBERSHIP_CHARGE_FAILURE, MEMBERSHIP_CHARGE_SUCCESS,
|
||||
} from './actions';
|
||||
|
||||
import { snakeToCamelCase } from './utils'
|
||||
|
||||
const initialState = {
|
||||
isAuthenticated: false,
|
||||
isLoggingIn: false,
|
||||
@@ -26,6 +28,17 @@ const initialState = {
|
||||
lastOrder: null,
|
||||
};
|
||||
|
||||
|
||||
function formatErrors(errs) {
|
||||
let _errs = {};
|
||||
for(let [key, value] of Object.entries(errs)) {
|
||||
_errs[snakeToCamelCase(key)] = value.join('\n');
|
||||
}
|
||||
return _errs
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default function duskenApp(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
/* Login */
|
||||
@@ -80,7 +93,7 @@ export default function duskenApp(state = initialState, action) {
|
||||
case REGISTER_USER_FAILURE:
|
||||
return Object.assign({}, state, {
|
||||
isRegisteringUser: false,
|
||||
registerError: action.registerError
|
||||
registerError: formatErrors(action.registerError)
|
||||
});
|
||||
|
||||
case REGISTER_USER_SUCCESS:
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export function snakeToCamelCase(val) {
|
||||
return val.replace(/_([a-z])/g, function (g) { return g[1].toUpperCase(); });
|
||||
}
|
||||
Reference in new issue
Block a user