I want to achieve a dynamically built form with controlled components that scale well. The problem I'm having is updating the state is causing the entire form to re-render (or something expensive?) causing epic type lag.
// Generate some example fields
let defaultFields = {};
for(let n=0; n<1000; n++){
defaultFields[n.toString()] = {'id':n.toString(), 'value':'','label':'Field '+n}
}
const [valuesById, setValuesById] = useState(defaultFields);
const updateValueCallback = React.useCallback((e)=>{
e.preventDefault();
e.persist();
setValuesById(prevValuesById => {
let fieldId = e.target.id;
return {...prevValuesById,
[fieldId]:{
'id':fieldId,
'value':e.target.value,
'label':'Field '+fieldId
}};
});
});
return <div>
{ Object.entries(valuesById).map(([id,formField]) => {
return <p key={formField.id}>
<label>{formField.label}
<SingleLineStringInput isRequired={false} value={formField.value} onChangeCallback={updateValueCallback} id={formField.id} name={'name_'+formField.id} />
</label>
</p>
})
}
</div>;
If the props aren't changing for 999 of the fields then why do they re-render? Or what is actually happening here (the fields don't actually flash in the debug tool but the parent does)? I really need help to understand this better and a fix which isn't too drastically different from what I've done as we've built a large amount of logic on top of this basic structure and have only now realised that it's not scaling.
SingleLineInput:
const SingleLineStringInput = React.memo(({name, id, value, onChangeCallback}) => {
console.log("UPDATING "+id);
return <input className={'input ' + inputClasses.join(' ')} name={name} id={id} type="text"
value={(value === null) ? '' : value}
onChange={onChangeCallback} />
});