Form Builder Using Flexible Compound Component in React
Dharani Jayakanthan - Danny
Posted on September 9, 2024
In this blog, we’ll explore how to build a form builder that updates the parent component as the user interacts with the form. This approach can be particularly useful in real-time validation, autosaving, or scenarios where you want to keep the parent state in sync with form inputs immediately as they change.
We will be leveraging Flexible Compound Component pattern for this form builder.
What is Flexible Compound Component
- Compound Components are patterns used to create flexible and reusable components where the parent component can control the behavior and render children dynamically.
- Compound components allow developers to separate logic and presentation, leading to cleaner, more maintainable code.
- This pattern promotes customization without compromising on the separation of concerns
- With flexible compound components, you can allow end-users of your component to configure their own behavior while keeping the core logic intact.
How the Compound Component Pattern Works in React
- The parent component serves as the core of the compound component, managing the state and providing methods or handlers that the child components can utilize. It passes data to its children either directly as props or, more commonly, through React’s Context API.
- Child components within the compound component structure are responsible for rendering individual pieces of the UI. They consume the state and behavior that the parent component provides, allowing for modularity and separation of concerns.
- The power of the compound component pattern lies in the composition of multiple child components within the parent component. You can add as many child components as necessary, and they will automatically work together due to the shared context and state from the parent.
Building a Form Builder using Flexible Compound Component: Step-by-Step
With the Flexible Compound Component Design Pattern, we can create a form that directly updates the parent state in real-time.
- The parent component holds the form state.
- Child components like Input, Checkbox, or Dropdown receive state from the parent and notify the parent when they change.
- Form field directly informs the parent of any change.
Let’s create a controlled form that updates the parent state as soon as any input field changes.
1. Setting Up the Parent Form
Component
The parent Form
component will manage the state and provide the state and onChange
handler to the child components via context. Each input field will update the form state in real-time.
import React, { useContext, useState } from 'react';
import { FormContext } from './context/formContext';
export const Form = ({ children, onFormChange }) => {
const [formData, setFormData] = useState({});
const updateField = (name, value) => {
const updatedData = {
...formData,
[name]: value,
};
setFormData(updatedData);
onFormChange(updatedData); // Notify parent about the form change immediately
};
return (
<FormContext.Provider value={{ formData, updateField }}>
<div>{children}</div> {/* No <form> tag, direct div usage */}
</FormContext.Provider>
);
};
Explanation:
- The
Form
component managesformData
internally. - The
updateField
function updates the state for the individual fields and immediately informs the parent of changes viaonFormChange
. - This structure updates the parent directly.
2. Creating the FormInput
Component
The FormInput
component will use the context to retrieve the form’s current state and update the value when the user types.
export function FormInput({ name, label, type = 'text' }) {
const { formData, updateField } = useContext(FormContext);
const handleInputChange = (e) => {
updateField(name, e.target.value);
};
return (
<div>
<label>{label}</label>
<input
type={type}
name={name}
value={formData[name] || ''}
onChange={handleInputChange}
/>
</div>
);
}
Here:
-
FormInput
uses theFormContext
to access the form’s state andupdateField
handler. - It immediately updates the field’s value and calls the parent’s
onFormChange
function through context as the user types.
3. Creating a FormCheckbox
Component
We can also create a checkbox component to showcase how to manage different form elements.
export function FormCheckbox({ name, label }) {
const { formData, updateField } = useContext(FormContext);
const handleCheckboxChange = (e) => {
updateField(name, e.target.checked);
};
return (
<div>
<label>
<input
type="checkbox"
checked={formData[name] || false}
onChange={handleCheckboxChange}
/>
{label}
</label>
</div>
);
}
- The checkbox works similarly to the
FormInput
but toggles betweentrue
andfalse
based on thechecked
property. - Again, it informs the parent about the state change instantly via the context.
4. Using the Form Components Together
Now let’s see how to use the form components in the parent component and handle the form changes.
function App() {
const [formState, setFormState] = useState({});
const handleFormChange = (updatedData) => {
setFormState(updatedData);
console.log('Updated form data:', updatedData);
};
return (
<div>
<h1>Controlled Form Builder</h1>
<Form onFormChange={handleFormChange}>
<FormInput name="firstName" label="First Name" />
<FormInput name="lastName" label="Last Name" />
<FormInput name="email" label="Email" type="email" />
<FormCheckbox name="acceptTerms" label="Accept Terms" />
</Form>
<pre>{JSON.stringify(formState, null, 2)}</pre> {/* Display current form state */}
</div>
);
}
export default App;
In this example:
-
App
component holds theformState
. - The
handleFormChange
function is passed to theForm
component and updates theformState
every time any input changes.
Extending the Form Builder
Now that we have a basic controlled form builder that updates the parent state on every change, let’s extend it by adding dynamic fields and validation in coming blogs.
Conclusion
The Flexible Compound Component Design Pattern in React allows you to build modular, controlled form components that can update their parent component in real-time. This pattern:
- Easy styling complex form UI's.
- Addition of any components in between the Form Component.
- Keeps the parent component in sync with the form state.
- Is flexible enough to handle dynamic fields, validation, and various input types.
This approach is ideal for scenarios where you need real-time form updates, like form builders, live-editing forms, and autosave applications. It offers a clean and maintainable structure that scales well with complexity.
Happy building!
Posted on September 9, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.