Ajout HOC WithForm

This commit is contained in:
wpetit 2020-02-25 17:21:55 +01:00
parent 85acbcbdb5
commit 07f0960c8c
3 changed files with 120 additions and 88 deletions

View File

@ -1,118 +1,77 @@
import { useState, useEffect } from 'react'; import { useEffect } from 'react';
import { WithForm } from './WithForm';
const defaultData = { export function UserForm({ form, onSubmit }) {
username: '',
password: '',
passwordVerification: ''
};
const defaultErrors = {
username: { hasError: false, message: '' },
password: { hasError: false, message: '' },
passwordVerification: { hasError: false, message: '' },
};
export function UserForm({ data = defaultData, errors = defaultErrors, onSubmit }) {
const validators = {
username: (value, formData) => {
if (value === "") return { hasError: true, message: 'Le nom d\'utilisateur ne peut pas être vide !' }
return { hasError: false, message: '' };
},
password: (value, formData) => {
if (value === "") return { hasError: true, message: 'Le mot de passe ne peut pas être vide !' }
if (value !== formData.passwordVerification) return { hasError: true, message: 'Vos deux mots de passe sont différents !' }
return { hasError: false, message: '' };
},
passwordVerification: (value, formData) => {
if (value !== formData.password) return { hasError: true, message: 'Vos deux mots de passe sont différents !' }
return { hasError: false, message: '' };
}
};
const [ formErrors, setFormErrors ] = useState({ ...defaultErrors, ...errors });
const [ formData, setFormData ] = useState({ ...defaultData, ...data });
useEffect(() => { useEffect(() => {
setFormErrors(formErrors => { console.log(form.valid, form.submitted);
return { ...formErrors, ...errors }; if (form.valid && form.submitted) onSubmit(form.data);
}); }, [form.valid, form.submitted]);
}, [errors]);
useEffect(() => {
setFormData(formData => {
return { ...formData, ...data };
});
}, [data]);
const hasFormErrors = Object.values(formErrors).reduce((hasFormErrors, fieldError) => {
return hasFormErrors || fieldError.hasError;
}, false);
const { username, password, passwordVerification } = formData;
const onInputChange = evt => {
const name = evt.target.name;
const value = evt.target.value;
setFormData(formData => {
return { ...formData, [name]: value };
});
}
useEffect(() => {
Object.keys(formData).forEach(name => {
if (typeof validators[name] !== 'function') return;
const value = formData[name];
const result = validators[name](value, formData);
setFormErrors(formErrors => {
return { ...formErrors, [name]: result };
});
});
}, [formData]);
const dispatchSubmit = evt => {
evt.preventDefault();
if (hasFormErrors) return;
if (typeof onSubmit === 'function') onSubmit(formData);
};
return ( return (
<form className="form" onSubmit={dispatchSubmit}> <form className="form" onSubmit={form.onSubmit}>
<div className="field"> <div className="field">
<label className="label" htmlFor="username">Nom d'utilisateur</label> <label className="label" htmlFor="username">Nom d'utilisateur</label>
<div className="control"> <div className="control">
<input className={`input ${formErrors.username.hasError ? 'is-danger' : ''}`} type="text" name="username" value={username} onChange={onInputChange} /> <input className={`input ${form.hasError('username') ? 'is-danger' : ''}`} type="text"
name="username" value={form.field('username', '')}
onChange={form.onChange('username')} />
</div> </div>
{ {
formErrors.username.hasError ? form.hasError('username') ?
<p className="help is-danger">{formErrors.username.message}</p> <p className="help is-danger">{form.error('username')}</p>
: null : null
} }
</div> </div>
<div className="field"> <div className="field">
<label className="label" htmlFor="password">Mot de passe</label> <label className="label" htmlFor="password">Mot de passe</label>
<div className="control"> <div className="control">
<input className={`input ${formErrors.password.hasError ? 'is-danger' : ''}`} type="password" name="password" autoComplete='new-password' value={password} onChange={onInputChange} /> <input className={`input ${form.hasError('password') ? 'is-danger' : ''}`}
type="password" name="password"
autoComplete='new-password'
value={form.field('password', '')}
onChange={form.onChange('password')} />
</div> </div>
{ {
formErrors.password.hasError ? form.hasError('password') ?
<p className="help is-danger">{formErrors.password.message}</p> <p className="help is-danger">{form.error('password')}</p>
: null : null
} }
</div> </div>
<div className="field"> <div className="field">
<label className="label" htmlFor="passwordVerification">Mot de passe (vérification)</label> <label className="label" htmlFor="passwordVerification">Mot de passe (vérification)</label>
<div className="control"> <div className="control">
<input className={`input ${formErrors.passwordVerification.hasError ? 'is-danger' : ''}`} type="password" name="passwordVerification" autoComplete='new-password' value={passwordVerification} <input className={`input ${form.hasError('passwordVerification') ? 'is-danger' : ''}`}
onChange={onInputChange} /> type="password" name="passwordVerification"
autoComplete='new-password'
value={form.field('passwordVerification', '')}
onChange={form.onChange('passwordVerification')} />
</div> </div>
{ {
formErrors.passwordVerification.hasError ? form.hasError('passwordVerification') ?
<p className="help is-danger">{formErrors.passwordVerification.message}</p> <p className="help is-danger">{form.error('passwordVerification')}</p>
: null : null
} }
</div> </div>
<button type="submit" disabled={hasFormErrors} className="button">Envoyer</button> <button type="submit" disabled={!form.valid} className="button">Envoyer</button>
</form> </form>
); );
}; };
const validators = {
username: (value, formData) => {
if (!value) return { hasError: true, message: 'Le nom d\'utilisateur ne peut pas être vide !' }
return { hasError: false, message: '' };
},
password: (value, formData) => {
if (!value) return { hasError: true, message: 'Le mot de passe ne peut pas être vide !' }
if (value !== formData.passwordVerification) return { hasError: true, message: 'Vos deux mots de passe sont différents !' }
return { hasError: false, message: '' };
},
passwordVerification: (value, formData) => {
if (value !== formData.password) return { hasError: true, message: 'Vos deux mots de passe sont différents !' }
return { hasError: false, message: '' };
}
};
export const ExtendedUserForm = WithForm(validators)(UserForm);

View File

@ -0,0 +1,73 @@
import { useState, useEffect } from 'react';
export function WithForm(validators) {
return function(Component) {
return function WithFormWrapper(props) {
const [ formData, setFormData ] = useState({});
const [ formErrors, setFormErrors ] = useState({});
const [ submitted, setSubmitted ] = useState(false);
const validateField = (name) => {
if (typeof validators[name] !== 'function') return;
const value = formData[name];
const result = validators[name](value, formData);
setFormErrors(formErrors => {
return { ...formErrors, [name]: result };
});
}
useEffect(() => {
Object.keys(formData).forEach(validateField);
}, [formData]);
useEffect(() => {
setSubmitted(false);
}, [submitted]);
const form = {
field: (name, defaultValue) => {
return name in formData ? formData[name] : defaultValue;
},
onChange: (name) => {
return (evt) => {
const value = evt.target.value;
setFormData(formData => {
return { ...formData, [name]: value };
});
};
},
hasError: (name) => {
return name in formErrors ? formErrors[name].hasError : false;
},
error: (name) => {
return name in formErrors ? formErrors[name].message : '';
},
onSubmit: (evt) => {
evt.preventDefault();
Object.keys(validators).forEach(validateField);
setSubmitted(true);
},
valid: !Object.values(formErrors).reduce((hasErrors, fieldError) => {
return hasErrors || fieldError.hasError;
}, false),
data: { ...formData },
errors: { ...formErrors },
submitted,
};
return <Component form={form} {...props} />;
};
}
}
// connect(mapStateToProps)(MyComponent)

View File

@ -1,13 +1,13 @@
import React from 'react' import React from 'react'
import Page from './page'; import Page from './page';
import { UserForm } from '../components/UserForm'; import { ExtendedUserForm as UserForm } from '../components/UserForm';
export default class HomePage extends React.PureComponent { export default class HomePage extends React.PureComponent {
render() { render() {
const onUserFormSubmit = formData => { const onUserFormSubmit = formData => {
console.log(formData); console.log(formData);
}; };
return ( return (
<Page title="home"> <Page title="home">