inertia
inertia copied to clipboard
values prop not honored by useForm
I'm using a generic component for Create/Update forms. The difference between the two is values
parameter passed to Update form.
const GenericForm: React.FC<GenericFormProps> = ({
fields,
values = null,
...
}) => {
const defaultValues = {};
for (let key of Object.keys(fields)) {
defaultValues[key] = getDefaultValueForType(fields[key]);
}
const { data, setData, post, put, processing, errors, reset } = useForm(values || defaultValues);
}
Now, this is called in the following manner for the two purposes: Create Form
<GenericForm fields={formFields}/>
Update Form
<GenericForm fields={formFields} values={currentValues}/>
However, when the form is rendered, the data
always contains the defaultValues
and never the values
. While this is OK for Create Form, it means losing the existing value for Update Form.
Also,
I've tried the following alternative for populating defaultValues
object:
const defaultValues = {};
for (let key of Object.keys(fields)) {
defaultValues[key] = values ? values[key] : getDefaultValueForType(fields[key]);
}
Log shows the defaultValues
is updated by the data within values
object, but data
for form remains the same.
Further, if I try to use setDefaults, like:
if (values) {
setDefaults(values);
}
then it results in Too many rerenders
; whereas, wrapping it in a useEffect
results in the original behavior mentioned in this issue.
This issue isn’t specific to Inertia; it’s a characteristic of React’s useState
.
The useForm
hook uses the initial form data passed to it to set up its internal state. This state is only initialized once when the component is first rendered. If you try to update the form data from outside the component by passing different values
props, it won’t affect the internal state of the useForm
hook after it’s been initialized.
If you want to do so, you need to write a useEffect
for it to update the data
whenever the props gets a new value:
useEffect(() => {
setData(values)
}, [values])
However, here you need to use useMemo
for the object that's passed to GenericForm
as the values
prop in the parent component of GenericForm
which renders it. Alternatively, you can use JSON.stringify(values)
as the dependency for useEffect
:
useEffect(() => {
setData(values)
}, [JSON.stringify(values)])
Another possible solution is to create a key based on the values
prop and pass that key to your GenericForm
component. This will force React to unmount and remount your component, effectively resetting the useForm
hook with the new values:
<GenericForm key={JSON.stringify(currentValues)} fields={formFields} values={currentValues}/>
Good pointers @Hasan-Mir. Closing this one since this is the expected behavior for state management in React.