如何在React.js中基于JSON数组渲染3个及以上动态表单
Hey there! Let's walk through how to render those dynamic grouped forms in React using your field configuration. First off, I noticed a tiny syntax issue in your fields array—you used curly braces instead of square brackets, and a couple of missing commas in the select_one objects. I'll fix those in the example below to make everything work smoothly.
Step 1: Clean Up & Structure Your Field Data
First, let's correct the field configuration to valid JS syntax:
const fields = [ { id: 1, type: 'group' }, { id: 2, type: 'text', label: 'Name', group_id: 1 }, { id: 3, type: 'text', label: 'Address', group_id: 1 }, { id: 4, type: 'text', label: 'City', value: 'Lahore', group_id: 1 }, { id: 5, type: 'text', label: 'State', value: 'Punjab', group_id: 1 }, { id: 6, type: 'text', label: 'Country', value: 'Pakistan', group_id: 1 }, { id: 7, type: 'group' }, { id: 8, type: 'date', label: 'Date of birth', group_id: 7 }, { id: 9, type: 'email', label: 'Email', group_id: 7 }, { id: 10, type: 'phone', label: 'Phone', group_id: 7 }, { id: 11, type: 'color', label: 'Color', value: '#ff9900', group_id: 7 }, { id: 12, type: 'number', label: 'Age', value: 22, group_id: 7 }, { id: 13, type: 'number', label: 'Friends', value: 3, group_id: 7 }, { id: 14, type: 'select_one', label: 'Select One City', value: 'karachi', choices: ['lahore', 'karachi', 'multan', 'faisalabad', 'pishawar', 'queta', 'islamabad'], group_id: 7 }, { id: 15, type: 'select_one', label: 'Select One Fruit', value: 'peach', choices: ['apple', 'banana', 'mango', 'peach', 'grapes', 'pine', 'strawberry'], group_id: 7 } ];
Step 2: Group Fields by Their group_id
We need to organize fields into their respective groups so we can render each group as a separate form section. Let's write a helper function to do this:
const groupFields = () => { const groups = {}; // First, collect all group objects fields.forEach(field => { if (field.type === 'group') { groups[field.id] = { ...field, fields: [] }; } }); // Then assign each field to its group fields.forEach(field => { if (field.type !== 'group' && field.group_id) { groups[field.group_id].fields.push(field); } }); // Convert the groups object to an array for easy rendering return Object.values(groups); };
Step 3: Create a Reusable Field Component
Let's build a component that renders the correct input type based on the field's type prop. This keeps our code DRY (Don't Repeat Yourself):
const FormField = ({ field, value, onChange }) => { const handleChange = (e) => { onChange(field.id, e.target.value); }; switch (field.type) { case 'text': case 'email': case 'phone': return ( <div className="form-field"> <label>{field.label}</label> <input type={field.type} value={value} onChange={handleChange} /> </div> ); case 'date': return ( <div className="form-field"> <label>{field.label}</label> <input type="date" value={value} onChange={handleChange} /> </div> ); case 'color': return ( <div className="form-field"> <label>{field.label}</label> <input type="color" value={value} onChange={handleChange} /> </div> ); case 'number': return ( <div className="form-field"> <label>{field.label}</label> <input type="number" value={value} onChange={handleChange} /> </div> ); case 'select_one': return ( <div className="form-field"> <label>{field.label}</label> <select value={value} onChange={handleChange}> {field.choices.map(choice => ( <option key={choice} value={choice}> {choice.charAt(0).toUpperCase() + choice.slice(1)} </option> ))} </select> </div> ); default: return null; } };
Step 4: Build the Main Form Component
Now we'll put it all together: manage form state, group the fields, and render each group with its fields.
import React, { useState, useEffect } from 'react'; const DynamicGroupedForm = () => { // Initialize form state with default values from fields const [formState, setFormState] = useState({}); useEffect(() => { const initialState = {}; fields.forEach(field => { initialState[field.id] = field.value ?? ''; }); setFormState(initialState); }, []); const handleFieldChange = (fieldId, newValue) => { setFormState(prev => ({ ...prev, [fieldId]: newValue })); }; const handleSubmit = (e) => { e.preventDefault(); // Do something with the form data, like submit to an API console.log('Form submitted:', formState); }; const groupedFields = groupFields(); return ( <form onSubmit={handleSubmit} className="grouped-form"> {groupedFields.map(group => ( <div key={group.id} className="form-group"> <h3>Group {group.id}</h3> {group.fields.map(field => ( <FormField key={field.id} field={field} value={formState[field.id]} onChange={handleFieldChange} /> ))} </div> ))} <button type="submit">Submit Form</button> </form> ); }; export default DynamicGroupedForm;
Step 5: Add Some Basic Styling (Optional)
To make the forms look clean, add a bit of CSS:
.grouped-form { max-width: 800px; margin: 2rem auto; padding: 0 1rem; } .form-group { border: 1px solid #eee; padding: 1.5rem; margin-bottom: 1.5rem; border-radius: 8px; } .form-field { margin-bottom: 1rem; } .form-field label { display: block; margin-bottom: 0.5rem; font-weight: 500; } .form-field input, .form-field select { width: 100%; padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px; } button { padding: 0.75rem 1.5rem; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; } button:hover { background: #0056b3; }
Quick Breakdown:
- Data Grouping: The
groupFieldsfunction organizes each field into its assigned group, making it easy to render each group as a distinct section. - Reusable Field Component:
FormFieldhandles all input types, so you don't have to repeat code for each field type. - State Management: We initialize the form state with the default values from your
fieldsarray, and update it whenever a field changes. - Scalability: If you add more groups or field types later, you just need to update the
FormFieldswitch case and add the new field to thefieldsarray—no major rewrites needed.
内容的提问来源于stack exchange,提问作者Kashif

