Skip to content

Commit 0bba8ac

Browse files
committed
Migrate Sign Up form to react-final-form
1 parent dcd2930 commit 0bba8ac

File tree

7 files changed

+215
-306
lines changed

7 files changed

+215
-306
lines changed

client/modules/User/actions.js

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,8 @@ export function authError(error) {
1212
};
1313
}
1414

15-
export function signUpUser(previousPath, formValues) {
16-
return (dispatch) => {
17-
apiClient.post('/signup', formValues)
18-
.then((response) => {
19-
dispatch({
20-
type: ActionTypes.AUTH_USER,
21-
user: response.data
22-
});
23-
dispatch(justOpenedProject());
24-
browserHistory.push(previousPath);
25-
})
26-
.catch((error) => {
27-
const { response } = error;
28-
dispatch(authError(response.data.error));
29-
});
30-
};
15+
export function signUpUser(formValues) {
16+
return apiClient.post('/signup', formValues);
3117
}
3218

3319
export function loginUser(formValues) {
@@ -41,6 +27,13 @@ export function loginUserSuccess(user) {
4127
};
4228
}
4329

30+
export function authenticateUser(user) {
31+
return {
32+
type: ActionTypes.AUTH_USER,
33+
user
34+
};
35+
}
36+
4437
export function loginUserFailure(error) {
4538
return {
4639
type: ActionTypes.AUTH_ERROR,

client/modules/User/components/LoginForm.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ function LoginForm(props) {
3636
onSubmit={validateAndLoginUser}
3737
>
3838
{({
39-
handleSubmit, pristine, submitting
39+
handleSubmit, pristine, submitting, invalid
4040
}) => (
4141
<form
4242
className="form"
@@ -78,7 +78,7 @@ function LoginForm(props) {
7878
</Field>
7979
<Button
8080
type="submit"
81-
disabled={submitting || pristine}
81+
disabled={submitting || invalid || pristine}
8282
>{props.t('LoginForm.Submit')}
8383
</Button>
8484
</form>
Lines changed: 136 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,152 @@
11
import PropTypes from 'prop-types';
22
import React from 'react';
33
import { withTranslation } from 'react-i18next';
4-
5-
import { domOnlyProps } from '../../../utils/reduxFormUtils';
4+
import { Form, Field } from 'react-final-form';
5+
import { useDispatch, useSelector } from 'react-redux';
6+
import { browserHistory } from 'react-router';
7+
import { validateSignup } from '../../../utils/reduxFormUtils';
8+
import { signUpUser, authenticateUser, authError } from '../actions';
69
import Button from '../../../common/Button';
10+
import { justOpenedProject } from '../../IDE/actions/ide';
11+
import apiClient from '../../../utils/apiClient';
12+
13+
function asyncValidate(fieldToValidate, value) {
14+
if (!value || value.trim().length === 0) return `Please enter a ${fieldToValidate}.`;
15+
const queryParams = {};
16+
queryParams[fieldToValidate] = value;
17+
queryParams.check_type = fieldToValidate;
18+
return apiClient.get('/signup/duplicate_check', { params: queryParams })
19+
.then((response) => {
20+
if (response.data.exists) {
21+
return response.data.message;
22+
}
23+
return '';
24+
});
25+
}
26+
27+
function validateUsername(username) {
28+
return asyncValidate('username', username);
29+
}
30+
31+
function validateEmail(email) {
32+
return asyncValidate('email', email);
33+
}
734

835
function SignupForm(props) {
9-
const {
10-
fields: {
11-
username, email, password, confirmPassword
12-
},
13-
handleSubmit,
14-
submitting,
15-
invalid,
16-
pristine,
17-
} = props;
36+
const dispatch = useDispatch();
37+
const previousPath = useSelector(state => state.ide.previousPath);
38+
function validateAndsignUpUser(formValues) {
39+
return new Promise((resolve, reject) => {
40+
signUpUser(formValues)
41+
.then((response) => {
42+
dispatch(authenticateUser(response.data));
43+
dispatch(justOpenedProject());
44+
browserHistory.push(previousPath);
45+
resolve();
46+
})
47+
.catch((error) => {
48+
const { response } = error;
49+
dispatch(authError(response.data.error));
50+
reject();
51+
});
52+
});
53+
}
54+
1855
return (
19-
<form
20-
className="form"
21-
onSubmit={handleSubmit(props.signUpUser.bind(this, props.previousPath))}
56+
<Form
57+
fields={['username', 'email', 'password', 'confirmPassword']}
58+
validate={validateSignup}
59+
onSubmit={validateAndsignUpUser}
2260
>
23-
<p className="form__field">
24-
<label htmlFor="username" className="form__label">{props.t('SignupForm.Title')}</label>
25-
<input
26-
className="form__input"
27-
aria-label={props.t('SignupForm.TitleARIA')}
28-
type="text"
29-
id="username"
30-
{...domOnlyProps(username)}
31-
/>
32-
{username.touched && username.error && (
33-
<span className="form-error">{username.error}</span>
34-
)}
35-
</p>
36-
<p className="form__field">
37-
<label htmlFor="email" className="form__label">{props.t('SignupForm.Email')}</label>
38-
<input
39-
className="form__input"
40-
aria-label={props.t('SignupForm.EmailARIA')}
41-
type="text"
42-
id="email"
43-
{...domOnlyProps(email)}
44-
/>
45-
{email.touched && email.error && (
46-
<span className="form-error">{email.error}</span>
47-
)}
48-
</p>
49-
<p className="form__field">
50-
<label htmlFor="password" className="form__label">{props.t('SignupForm.Password')}</label>
51-
<input
52-
className="form__input"
53-
aria-label={props.t('SignupForm.PasswordARIA')}
54-
type="password"
55-
id="password"
56-
{...domOnlyProps(password)}
57-
/>
58-
{password.touched && password.error && (
59-
<span className="form-error">{password.error}</span>
60-
)}
61-
</p>
62-
<p className="form__field">
63-
<label htmlFor="confirm password" className="form__label">{props.t('SignupForm.ConfirmPassword')}</label>
64-
<input
65-
className="form__input"
66-
type="password"
67-
aria-label={props.t('SignupForm.ConfirmPasswordARIA')}
68-
id="confirm password"
69-
{...domOnlyProps(confirmPassword)}
70-
/>
71-
{confirmPassword.touched && confirmPassword.error && (
72-
<span className="form-error">{confirmPassword.error}</span>
73-
)}
74-
</p>
75-
<Button
76-
type="submit"
77-
disabled={submitting || invalid || pristine}
78-
>{props.t('SignupForm.SubmitSignup')}
79-
</Button>
80-
</form>
61+
{({
62+
handleSubmit, pristine, submitting, invalid
63+
}) => (
64+
<form
65+
className="form"
66+
onSubmit={handleSubmit}
67+
>
68+
<Field name="username" validate={validateUsername} validateFields={[]}>
69+
{field => (
70+
<p className="form__field">
71+
<label htmlFor="username" className="form__label">{props.t('SignupForm.Title')}</label>
72+
<input
73+
className="form__input"
74+
aria-label={props.t('SignupForm.TitleARIA')}
75+
type="text"
76+
id="username"
77+
{...field.input}
78+
/>
79+
{field.meta.touched && field.meta.error && (
80+
<span className="form-error">{field.meta.error}</span>
81+
)}
82+
</p>
83+
)}
84+
</Field>
85+
<Field name="email" validate={validateEmail} validateFields={[]}>
86+
{field => (
87+
<p className="form__field">
88+
<label htmlFor="email" className="form__label">{props.t('SignupForm.Email')}</label>
89+
<input
90+
className="form__input"
91+
aria-label={props.t('SignupForm.EmailARIA')}
92+
type="text"
93+
id="email"
94+
{...field.input}
95+
/>
96+
{field.meta.touched && field.meta.error && (
97+
<span className="form-error">{field.meta.error}</span>
98+
)}
99+
</p>
100+
)}
101+
</Field>
102+
<Field name="password">
103+
{field => (
104+
<p className="form__field">
105+
<label htmlFor="password" className="form__label">{props.t('SignupForm.Password')}</label>
106+
<input
107+
className="form__input"
108+
aria-label={props.t('SignupForm.PasswordARIA')}
109+
type="password"
110+
id="password"
111+
{...field.input}
112+
/>
113+
{field.meta.touched && field.meta.error && (
114+
<span className="form-error">{field.meta.error}</span>
115+
)}
116+
</p>
117+
)}
118+
</Field>
119+
<Field name="confirmPassword">
120+
{field => (
121+
<p className="form__field">
122+
<label htmlFor="confirm password" className="form__label">{props.t('SignupForm.ConfirmPassword')}</label>
123+
<input
124+
className="form__input"
125+
type="password"
126+
aria-label={props.t('SignupForm.ConfirmPasswordARIA')}
127+
id="confirm password"
128+
{...field.input}
129+
/>
130+
{field.meta.touched && field.meta.error && (
131+
<span className="form-error">{field.meta.error}</span>
132+
)}
133+
</p>
134+
)}
135+
</Field>
136+
<Button
137+
type="submit"
138+
disabled={submitting || invalid || pristine}
139+
>{props.t('SignupForm.SubmitSignup')}
140+
</Button>
141+
</form>
142+
143+
)}
144+
</Form>
81145
);
82146
}
83147

84148
SignupForm.propTypes = {
85-
fields: PropTypes.shape({
86-
username: PropTypes.objectOf(PropTypes.shape()).isRequired,
87-
email: PropTypes.objectOf(PropTypes.shape()).isRequired,
88-
password: PropTypes.objectOf(PropTypes.shape()).isRequired,
89-
confirmPassword: PropTypes.objectOf(PropTypes.shape()).isRequired,
90-
}).isRequired,
91-
handleSubmit: PropTypes.func.isRequired,
92-
signUpUser: PropTypes.func.isRequired,
93-
submitting: PropTypes.bool,
94-
invalid: PropTypes.bool,
95-
pristine: PropTypes.bool,
96-
previousPath: PropTypes.string.isRequired,
97149
t: PropTypes.func.isRequired
98150
};
99151

100-
SignupForm.defaultProps = {
101-
submitting: false,
102-
pristine: true,
103-
invalid: false,
104-
};
105-
106152
export default withTranslation()(SignupForm);

0 commit comments

Comments
 (0)