CMU Coding Bootcamp
at main 109 lines 6.5 kB view raw
1import { useState, useCallback } from "react"; // Import useState for managing state and useCallback for memoizing functions. 2 3export function useForm(initialValues = {}, validate = () => ({})) { // A custom hook for managing form state and validation. Takes initial form values and a validation function as arguments. If no validation function is provided, a default empty object is used, effectively skipping validation. 4 const [values, setValues] = useState(initialValues); // State for storing form field values. Initialized with initialValues. 5 const [errors, setErrors] = useState({}); // State for storing validation errors. Initialized as an empty object. 6 const [touched, setTouched] = useState({}); // State for tracking which fields have been touched/blurred. Initialized as an empty object. Not used in the current implementation 7 const [isSubmitting, setIsSubmitting] = useState(false); // State for tracking form submission status. Initialized to false. 8 9 // useCallback for memoizing the handleChange function 10 const handleChange = useCallback( 11 (e) => { // Function to handle changes in form fields. Takes an event object as an argument. 12 const { name, value } = e.target; // Extract name and value from the event target. 13 setValues((prev) => ({ ...prev, [name]: value })); // Update the values state with the new value, using name as the key. 14 15 // Clear the error for the field being modified (provides immediate feedback to the user) 16 if (errors[name]) { // If there's an existing error for this field 17 setErrors((prev) => ({ ...prev, [name]: "" })); // Clear the error for this field by setting it to an empty string. 18 } 19 }, 20 [errors] // Dependency array: This function depends on the errors state. 21 ); 22 23 // useCallback for memoizing the handleBlur function 24 const handleBlur = useCallback( 25 (e) => { // Function to handle blur events (when a field loses focus). Takes event object 26 const { name } = e.target; //get name from event target 27 setTouched((prev) => ({ ...prev, [name]: true })); // Update the touched state to mark the field as touched. Not used in current implementation 28 29 // Validate field on blur 30 const fieldErrors = validate({ [name]: values[name] }); // Validate only the blurred field using the provided validate function. 31 setErrors((prev) => ({ ...prev, ...fieldErrors })); // Update the errors state with any validation errors. 32 }, 33 [values, validate] // Dependency array: This function depends on the values state and the validate function. 34 ); 35 36 // useCallback for memoizing the handleSubmit function 37 const handleSubmit = useCallback( 38 async (onSubmit) => { // Function to handle form submission. Takes an onSubmit callback function as an argument. 39 setIsSubmitting(true); // Set isSubmitting to true to indicate form submission is in progress. Can be used to disable submit button, show loading indicator, etc. 40 41 // Validate all fields 42 const formErrors = validate(values); // Validate all form field values using the validate function. 43 setErrors(formErrors); // Update the errors state with all validation errors. 44 45 if (Object.keys(formErrors).length === 0) { // If there are no validation errors 46 try { 47 await onSubmit(values); // Call the provided onSubmit function with the form values. This assumes onSubmit is an async function. 48 setValues(initialValues); // Reset the form to its initial values after successful submission. 49 setTouched({}); //reset touched 50 } catch (error) { //catch any error during onSubmit 51 setErrors((prev) => ({ ...prev, submit: error.message })); // If an error occurs during submission, set a 'submit' error in the errors state. 52 } 53 } 54 55 setIsSubmitting(false); // Set isSubmitting back to false after submission is complete (successful or not). 56 }, 57 [values, validate, initialValues] // Dependency array: This function depends on values, validate, and initialValues. 58 ); 59 60 const reset = useCallback(() => { //function to reset form 61 setValues(initialValues); //reset values to initialValues 62 setErrors({}); //clear errors 63 setTouched({}); //clear touched fields 64 setIsSubmitting(false); //set isSubmitting to false 65 }, [initialValues]); // Dependency array: This function depends on initialValues. 66 67 // Validation helper for edge cases (this function performs additional validation) 68 const enhancedValidate = useCallback( 69 (fieldValues) => { //takes an object of field values as input 70 const newErrors = {}; //create empty object to store errors 71 72 for (const field in fieldValues) { //iterate over each field in fieldValues 73 const value = fieldValues[field]; //get the value of the field 74 75 // Generic validation for empty fields 76 if (!value?.trim()) { //if value is empty or contains only whitespace 77 newErrors[field] = `${field} is required.`; //set error message that field is required 78 continue; // Continue to the next field (important optimization: avoids unnecessary checks if a field is already invalid) 79 } 80 81 // Specific validation for email fields 82 if ( 83 field === "email" && //if field is email and value is not a valid email 84 !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value) 85 ) { 86 newErrors[field] = "Invalid email address."; //set error message for invalid email 87 continue; //continue to next field 88 } 89 90 // Add additional field-specific validations as needed Placeholder for adding more validation rules. 91 } 92 93 return newErrors; //return errors object 94 }, 95 [] //empty dependency array since this function doesn't depend on any external values 96 ); 97 98 return { //return an object containing form related data and functions 99 values, //form field values 100 errors, //validation errors 101 touched, //touched fields, not used in current implementation but can be used to conditionally display error messages after a field has been touched 102 isSubmitting, //submission status 103 handleChange, //function to handle field changes 104 handleBlur, //function to handle blur events 105 handleSubmit, //function to handle form submission 106 reset, //function to reset form 107 validate: enhancedValidate, //the enhanced validation function 108 }; 109}