Skip to content

Instantly share code, notes, and snippets.

@Arkellys
Last active November 15, 2022 06:29
Show Gist options
  • Save Arkellys/804c9f31dbda256efeefd0c9a5b6c65c to your computer and use it in GitHub Desktop.
Save Arkellys/804c9f31dbda256efeefd0c9a5b6c65c to your computer and use it in GitHub Desktop.
Custom hook to handle forms' submit with custom validators compatible with react-bootstrap (v2.5.0)
import { isEmpty } from "lodash";
import { useState } from "react";
/**
* Submits a form.
* @callback Submit
* @param {import("react").SyntheticEvent} event - Form event
* @param {Function} callback - Method called when the form is valid on submit
*/
/**
* Checks the validity of field(s) according to the validation schema.
* @callback CheckValidity
* @param {Object.<string, (string|number)>} [data={}] - Data of the fields to check the validity from
*/
/**
* @typedef {object} UseForm
* @property {Submit} submit - Method to submit a form
* @property {CheckValidity} checkValidity - Method to check the validity of the given field(s) according to the validation schema
* @property {boolean} isValid - Whether the form is valid
* @property {Object.<string, any>} validationErrors - Object containing the validation errors
*/
/**
* Provides a form's submit method with custom validators support.
* @param {Object.<string, Function[]>} [validationSchema={}] - List of fields with their custom validators
*
* @example
* const { submit, isValid, validationErrors } = useForm({
* username: [vRequired]
* password: [vRequired, vPassword],
* email: [vRequired, vEmail]
* });
*
* @returns {UseForm} Submit method and form status info
*/
const useForm = (validationSchema = {}) => {
const [validationErrors, setValidationErrors] = useState({});
const isValid = isEmpty(validationErrors);
const _getFormData = event => {
const formData = new FormData(event.target);
return Object.fromEntries(formData.entries());
};
const checkValidity = (data = {}) => {
const errors = {};
Object.entries(validationSchema).forEach(([field, validators]) => {
if (!validators || isEmpty(validators)) return false;
const value = data[field];
for (const validator of validators) {
const validationError = validator(value);
if (validationError) {
errors[field] = validationError;
return; // Stops at the first failed validator
}
}
});
setValidationErrors(errors);
return errors;
};
const submit = async (event, callback) => {
event.preventDefault();
const data = _getFormData(event);
const errors = checkValidity(data);
if (isEmpty(errors)) callback(data);
};
return {
submit,
checkValidity,
isValid,
validationErrors,
};
};
export default useForm;
import { Button, Form } from "react-bootstrap";
import { vRequired } from "./validators";
import useForm from "./useForm";
const FormExample = () => {
const { submit, isValid, validationErrors } = useForm({
username: [vRequired],
password: [vRequired]
});
const handleSubmit = event => {
submit(event, ({ username, password }) => {
// API call...
});
};
return (
<Form
onSubmit={handleSubmit}
noValidate
validated={isValid ? null : false}
>
<Form.Group controlId="username">
<Form.Control
type="text"
placeholder="Username"
name="username"
isInvalid={validationErrors.username}
/>
<Form.Control.Feedback type="invalid">
{validationErrors.username}
</Form.Control.Feedback>
</Form.Group>
<Form.Group controlId="password">
<Form.Control
type="password"
placeholder="Password"
name="password"
isInvalid={validationErrors.password}
/>
<Form.Control.Feedback type="invalid">
{validationErrors.password}
</Form.Control.Feedback>
</Form.Group>
<Button type="submit">
Submit
</Button>
</Form>
);
};
export default FormExample;
export const vRequired = (value) => {
if (value && value.length > 0) return null;
return "This field is required.";
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment