Managing forms in React can often lead to repetitive code, especially when handling input validation, state management, and submission logic. Creating a custom hook for form handling can encapsulate this logic, making your code cleaner, more reusable, and easier to maintain. In this post, we’ll walk through building a custom hook for form management.
Why Use a Custom Hook for Forms?
A custom hook allows you to:
- Encapsulate form logic: Keep form-related logic separate from your components.
- Reuse across components: Use the same form logic in multiple forms throughout your application.
- Simplify component code: Reduce the complexity of individual form components by moving logic into the custom hook.
Now, let’s create a custom hook named useForm
that manages form state and handles validation.
import { useState } from 'react';
const useForm = (initialValues, validate) => {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const handleChange = (event) => {
const { name, value } = event.target;
setValues({
...values,
[name]: value,
});
// Validate the field
if (validate) {
const fieldErrors = validate(name, value);
setErrors({
...errors,
...fieldErrors,
});
}
};
const handleSubmit = (event, callback) => {
event.preventDefault();
if (Object.keys(errors).length === 0) {
callback(values);
}
};
return {
values,
errors,
handleChange,
handleSubmit,
};
};
export default useForm;
So, let’s break down out custom hook and see what’s going on. Looking our code we can see the following:
- State Management:
- The hook starts by initializing two pieces of state:
values
(which stores form input values) anderrors
(to store validation errors). initialValues
are passed in as a parameter, allowing the hook to be used for various forms with different initial states.
2. handleChange Function:
- This function updates the form’s values whenever the user types in an input field.
- It retrieves the input’s
name
andvalue
from the event object and updates thevalues
state accordingly. - After updating the input value, it calls the
validate
function (if provided) to check for errors and update theerrors
state.
3. handleSubmit Function:
- This function is called when the form is submitted.
- It prevents the default form submission and checks if there are any errors present.
- If there are no errors, it calls the provided callback function with the current values, allowing the parent component to handle the form data.
📝 Note: Before moving on I’d like to clarify something about the hook. While this implementation is functional, it is essential to note that it might have some issues and this is just for learning purposes.
Validation Logic: The validate
function assumes that errors are returned as an object, but this can vary based on how you implement your validation logic. Make sure to adapt it according to your validation strategy (e.g., using a library like Yup).
Error Handling: Currently, the hook only validates inputs on change. You might want to validate all fields on submission as well to catch any remaining errors.
Dynamic Field Support: The hook can be enhanced to support dynamically added fields, such as in cases of multi-step forms or dynamically generated inputs.
Reset Functionality: You could add a method to reset the form to its initial state after submission or for other use cases.
And now, let’s use this custom hook in a functional component.
import React from 'react';
import useForm from './useForm';
const validate = (name, value) => {
const errors = {};
if (!value) {
errors[name] = 'This field is required';
}
return errors;
};
const MyForm = () => {
const { values, errors, handleChange, handleSubmit } = useForm(
{ username: '', email: '' },
validate
);
const onSubmit = (data) => {
// Here goes the logic to send the data to an API
console.log('Form Submitted:', data);
};
return (
<form onSubmit={(event) => handleSubmit(event, onSubmit)}>
<div>
<label>Username</label>
<input
type="text"
name="username"
value={values.username}
onChange={handleChange}
/>
{errors.username && <p>{errors.username}</p>}
</div>
<div>
<label>Email</label>
<input
type="email"
name="email"
value={values.email}
onChange={handleChange}
/>
{errors.email && <p>{errors.email}</p>}
</div>
<button type="submit">Submit</button>
</form>
);
};
So, the usage is pretty straightforward once we already understand the logic behind our custom hook. But if still unclear, let’s check quickly what’s going on in our implementation code.
- Validation Logic:
- A simple validation function is created that checks if the input value is present. If not, it returns an error message associated with the input’s name.
2. Using the Hook:
- The
useForm
hook is called with initial values forusername
andemail
, along with the validation function. - The returned properties (
values
,errors
,handleChange
, andhandleSubmit
) are destructured for easy access.
3. Rendering the Form:
- The form consists of two input fields for
username
andemail
. Each input field uses thehandleChange
method to update its value in the state. - Error messages are displayed below each input if validation fails.
4. Handling Form Submission:
- The
handleSubmit
method prevents the default form submission and validates the data. If no errors are present, it callsonSubmit
, where you can process the form data (e.g., sending it to an API).
Conclusion
Creating a custom hook for form handling in React can simplify your codebase and enhance reusability. By encapsulating form state and validation logic, you can create clean, maintainable components that are easier to work with as your application scales. While this implementation provides a solid foundation, consider potential enhancements for flexibility and robustness in real-world applications. My personal recomendation is to go with alternatives like React Hook Forms and Yup that will let you focus only on the actual logic of your form and will save you time from annoying bugs and cover scenarios that might not come up to your head.
Web developer with over ~6 years of experience. I am a highly motivated and results-oriented developer with a passion for creating scalable and user-friendly web applications. I am recently exploring the world of blogging, hoping to share some of my experience in this exciting and ever-evolving journey of development.