Doing any freelancing? want to finish the task quickly? Just follow below, apply on any forms and finsh form validation quickly.
Create a reducer
const reducer = (state, action) => {
switch (action.type) {
case 'SET_VALUE':
return {
...state,
[FORMDATA]: action.payLoad,
};
case 'SET_ERROR':
return {
...state,
[ERRORS]: action.payLoad,
};
case 'ON_CHANGE':
return {
...state,
...action.payLoad,
};
default:
return state;
}
};
Use the below custom hook
export const useForm = (schema, defaultFormValues = {}) => {
const [state, dispatch] = useReducer(reducer, { [FORMDATA]: defaultFormValues, [ERRORS]: {} });
const { formData, errors } = state;
// Commit state result
const dispatchValue = (field, value, result) => {
if (result instanceof Yup.ValidationError) {
// Error
errors[field] = result.message;
} else {
// Delete current state result
delete errors[field];
}
// Update state, for object update, need a clone
const newState = { [ERRORS]: errors, [FORMDATA]: { ...formData, [field]: value } };
dispatch({ type: 'ON_CHANGE', payLoad: newState });
};
const updateFormValue = (field, value) => {
// Validate the field, then before catch, if catch before then, both will be triggered
Yup.reach(schema(), field)
.validate(value)
.then((result) => {
dispatchValue(field, value, result);
})
.catch((result) => {
dispatchValue(field, value, result);
});
};
return {
validate: async () => {
const validationResult = { isValid: true, errors: {} };
try {
await schema().validate(formData, {
strict: true,
abortEarly: false,
stripUnknown: false,
});
} catch (e) {
const formValidationError = {};
if (e instanceof Yup.ValidationError) {
// eslint-disable-next-line no-restricted-syntax
for (const error of e.inner) {
// Only show the first error of the field
if (!formValidationError[error.path]) {
formValidationError[error.path] = error.message;
}
}
}
dispatch({ type: 'SET_ERROR', payLoad: formValidationError });
validationResult.isValid = false;
validationResult.errors = formValidationError;
return validationResult;
}
return validationResult;
},
setValue: (formInput) => {
// In case where we need to set all the form values
if (Array.isArray(formInput)) {
const [defaultObj] = formInput;
dispatch({ type: 'SET_VALUE', payLoad: { ...formData, ...defaultObj } });
} else {
const { name, value } = formInput;
updateFormValue(name, value);
}
},
errors: (field) => errors[field],
getValues: () => formData,
value: (field) => state[field],
onFormBlur: (name, value) => {
updateFormValue(name, value);
},
};
};
To handle form value and validation just import the above hook.
const { validate, setValue, errors, getValues } = useForm(validateSchema, formData);
You can use yup to define the schema
import * as yup from 'yup';
// eslint-disable-next-line import/prefer-default-export
export const validateSchema = () =>
yup.object().shape({
service: yup
.number()
.required('Please provide the service')
});
To get the form values you can use the method exported from useFrom hook like below.
const initialState = getValues();
If you are using material-ui then use like below.
<TextField
id={ROLE}
name={ROLE}
label="Role"
placeholder=""
maxLength="15"
fullWidth
required
margin="dense"
value={initialState[ROLE]}
autoComplete="off"
disabled={isLoading || isSubmiting || !initialState[SERVICE]}
onChange={onChangeInput(ROLE)}
error={!!errors(ROLE)}
helperText={errors(ROLE)}
InputLabelProps={{
shrink: true,
'aria-label': 'Select Role',
}}
/>
If you have any suggetions add comments.... happy coding!!
Top comments (0)