Building Reusable and Dynamic Forms in React


Introduction
Building dynamic and interactive forms in React can be challenging, especially when managing state and validation for complex forms. With React's powerful state management capabilities and hooks, creating forms becomes more efficient and streamlined. In this post, we’ll explore how to build a reusable form component with validation and state management using React.
Setting Up the Form Component
Let’s start by creating a reusable FormInput component to handle individual input fields.
import React from 'react'; const FormInput = ({ label, type, value, onChange, error }) => { return ( <div> <label>{label}</label> <input type={type} value={value} onChange={onChange} style={{ borderColor: error ? 'red' : '#ccc' }} /> {error && <small style={{ color: 'red' }}>{error}</small>} </div> ); }; export default FormInput;
This component includes a label, an input field, and error handling for validation feedback. It accepts props like label
, type
, value
, onChange
, and error
to make it reusable for various form fields.
Managing Form State and Validation
Next, we’ll create a parent form component to handle state and validation logic.
import React, { useState } from 'react'; import FormInput from './FormInput'; const Form = () => { const [formData, setFormData] = useState({ username: '', email: '', password: '', }); const [errors, setErrors] = useState({}); const validate = () => { const newErrors = {}; if (!formData.username) newErrors.username = 'Username is required'; if (!formData.email.includes('@')) newErrors.email = 'Email is invalid'; if (formData.password.length < 6) newErrors.password = 'Password must be at least 6 characters'; return newErrors; }; const handleChange = (e) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value }); }; const handleSubmit = (e) => { e.preventDefault(); const validationErrors = validate(); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); } else { console.log('Form submitted:', formData); setErrors({}); } }; return ( <form onSubmit={handleSubmit}> <FormInput label="Username" type="text" value={formData.username} onChange={(e) => handleChange(e)} error={errors.username} name="username" /> <FormInput label="Email" type="email" value={formData.email} onChange={(e) => handleChange(e)} error={errors.email} name="email" /> <FormInput label="Password" type="password" value={formData.password} onChange={(e) => handleChange(e)} error={errors.password} name="password" /> <button type="submit">Submit</button> </form> ); }; export default Form;
In this component, we manage the form state using useState
and validate
inputs with a custom validate function. Errors are displayed dynamically based on the validation results.
Styling the Form
Let’s add some basic styling to enhance the form's appearance.
form { max-width: 400px; margin: 0 auto; padding: 20px; border: 1px solid #ccc; border-radius: 8px; background-color: #f9f9f9; } div { margin-bottom: 15px; } label { display: block; margin-bottom: 5px; font-weight: bold; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; }
This styling gives the form a clean and professional look, making it more user-friendly.
Enhancing with Custom Hooks
For better scalability, you can use a custom hook to handle form state and validation logic.
import { useState } from 'react'; const useForm = (initialState, validate) => { const [formData, setFormData] = useState(initialState); const [errors, setErrors] = useState({}); const handleChange = (e) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value }); }; const handleSubmit = (callback) => (e) => { e.preventDefault(); const validationErrors = validate(formData); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); } else { setErrors({}); callback(); } }; return { formData, errors, handleChange, handleSubmit }; }; export default useForm;
That's how you have to setup useForm
hook.
Conclusion
Creating reusable and dynamic forms in React not only simplifies your code but also improves maintainability. By leveraging components, hooks, and validation logic, you can build scalable forms for any application. Experiment with these techniques in your next React project and experience the benefits of cleaner and more efficient form management!