I'm relatively new with React functional components and I'm trying to build a system that builds forms out of an array of objects. It works so far, but I feel like I am missing something. Here is the code:
import React, { useState } from 'react';
import "./DynamicForm.css";
function DynamicForm(props) {
const [formFields, setFormFields] = useState([
{ name: "first_name", type: "text", value: "" },
{ name: "last_name", type: "text", value: "" },
{ name: "phone", type: "tel", value: "" },
{ name: "email", type: "email", value: "" },
{ name: "date_of_birth", type: "date", value: "" },
{ name: "language", type: "select", options: ['en', 'es'], value: "" },
])
const handleChange = (index, value) => {
let newFormFields = [...formFields];
newFormFields[index].value = value;
setFormFields(newFormFields);
}
const builtForm = (
<div>
{formFields.map((field, index) => {
return (
<div class="col-md-3" index={index}>
<div class="form-group">
<label class="control-label">{field.name}</label>
<input placeholder="" type={field.type} class="form-control" value={field.value} onChange={(e) => handleChange(index, e.target.value)} />
</div>
</div>
)
})}
</div>
)
return (
<div>
<section>
{builtForm}
</section>
</div>);
}
export default DynamicForm;
Note: I cut the code to the core to improve readability.
Isn't it a bit too simple? Every time I enter a new characters in one of the generated inputs, the whole form with all its fields re-renders again, am I right? Should I use other hooks or design it otherwise?
I'm not too sure about creating components dynamically like this without having the entire component update but here are some thoughts.
First up, in JSX class should be className. class is already a keyword in JavaScript so we use className, I believe this is what it is called in the virtual dom which is what you are actually editing/creating there.
Second, provide the key prop to components created by a loop. This helps with what is called reconciliation. Helping react recognize what components actually need updating. In your case, your fields all have a unique name prop, so you might change the div inside the map to this:
return (
<div key={field.name} className='col-md-3' index={index}>
...
</div>
);
Never use the index of the loop for the key.
Next, for this approach, to avoid the rerender of all inner components, I would want to use the useMemo hook to cache the elements. The issue is that you would create the component in a loop, which goes against "rules of hooks".
In the end, my approach would be to create a separate component for each field rather than creating them via a loop. However, I'd love to hear how some more experienced people might approach this to achieve the dynamic approach.