Last active
November 15, 2022 06:29
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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