Skip to content

Commit 299e8b1

Browse files
authored
Merge pull request marmelab#11099 from Jszigeti/fix-resetOptions-not-passed-to-reset
Fix `<Form>` ignores resetOptions when record changes
2 parents cd5442e + 8322e0c commit 299e8b1

File tree

2 files changed

+134
-2
lines changed

2 files changed

+134
-2
lines changed

packages/ra-core/src/form/Form.spec.tsx

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,129 @@ describe('Form', () => {
120120
expect(screen.getByText('isDirty: false')).not.toBeNull();
121121
});
122122

123+
it('should keep dirty values when record changes and resetOptions.keepDirtyValues is true', async () => {
124+
const { rerender } = render(
125+
<CoreAdminContext>
126+
<Form
127+
onSubmit={jest.fn()}
128+
record={{ id: 1, name: 'Initial' }}
129+
resetOptions={{ keepDirtyValues: true }}
130+
>
131+
<Input source="name" />
132+
</Form>
133+
</CoreAdminContext>
134+
);
135+
136+
expect(screen.getByDisplayValue('Initial')).not.toBeNull();
137+
138+
fireEvent.change(screen.getByLabelText('name'), {
139+
target: { value: 'User Modified' },
140+
});
141+
142+
await waitFor(() => {
143+
expect(screen.getByDisplayValue('User Modified')).not.toBeNull();
144+
});
145+
146+
rerender(
147+
<CoreAdminContext>
148+
<Form
149+
onSubmit={jest.fn()}
150+
record={{
151+
id: 1,
152+
name: 'Server Updated',
153+
otherField: 'new value',
154+
}}
155+
resetOptions={{ keepDirtyValues: true }}
156+
>
157+
<Input source="name" />
158+
</Form>
159+
</CoreAdminContext>
160+
);
161+
162+
await waitFor(() => {
163+
expect(screen.getByDisplayValue('User Modified')).not.toBeNull();
164+
});
165+
expect(screen.queryByDisplayValue('Server Updated')).toBeNull();
166+
});
167+
168+
it('should NOT keep dirty values when record changes and resetOptions.keepDirtyValues is false', async () => {
169+
const { rerender } = render(
170+
<CoreAdminContext>
171+
<Form onSubmit={jest.fn()} record={{ id: 1, name: 'Initial' }}>
172+
<Input source="name" />
173+
</Form>
174+
</CoreAdminContext>
175+
);
176+
177+
fireEvent.change(screen.getByLabelText('name'), {
178+
target: { value: 'User Modified' },
179+
});
180+
181+
await waitFor(() => {
182+
expect(screen.getByDisplayValue('User Modified')).not.toBeNull();
183+
});
184+
185+
rerender(
186+
<CoreAdminContext>
187+
<Form
188+
onSubmit={jest.fn()}
189+
record={{
190+
id: 1,
191+
name: 'Server Updated',
192+
}}
193+
>
194+
<Input source="name" />
195+
</Form>
196+
</CoreAdminContext>
197+
);
198+
199+
await waitFor(() => {
200+
expect(screen.getByDisplayValue('Server Updated')).not.toBeNull();
201+
});
202+
expect(screen.queryByDisplayValue('User Modified')).toBeNull();
203+
});
204+
205+
it('should NOT keep dirty values when switching to a different record even with resetOptions.keepDirtyValues', async () => {
206+
const { rerender } = render(
207+
<CoreAdminContext>
208+
<Form
209+
onSubmit={jest.fn()}
210+
record={{ id: 1, name: 'Record 1' }}
211+
resetOptions={{ keepDirtyValues: true }}
212+
>
213+
<Input source="name" />
214+
</Form>
215+
</CoreAdminContext>
216+
);
217+
218+
fireEvent.change(screen.getByLabelText('name'), {
219+
target: { value: 'User Modified Record 1' },
220+
});
221+
222+
await waitFor(() => {
223+
expect(
224+
screen.getByDisplayValue('User Modified Record 1')
225+
).not.toBeNull();
226+
});
227+
228+
rerender(
229+
<CoreAdminContext>
230+
<Form
231+
onSubmit={jest.fn()}
232+
record={{ id: 2, name: 'Record 2' }}
233+
resetOptions={{ keepDirtyValues: true }}
234+
>
235+
<Input source="name" />
236+
</Form>
237+
</CoreAdminContext>
238+
);
239+
240+
await waitFor(() => {
241+
expect(screen.getByDisplayValue('Record 2')).not.toBeNull();
242+
});
243+
expect(screen.queryByDisplayValue('User Modified Record 1')).toBeNull();
244+
});
245+
123246
it('should update Form state on submit', async () => {
124247
let isSubmitting;
125248

packages/ra-core/src/form/useAugmentedForm.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export const useAugmentedForm = <RecordType = any>(
4343
defaultValues,
4444
formRootPathname,
4545
resolver,
46+
resetOptions,
4647
reValidateMode = 'onChange',
4748
onSubmit,
4849
sanitizeEmptyValues,
@@ -86,9 +87,17 @@ export const useAugmentedForm = <RecordType = any>(
8687
const { reset, formState } = form;
8788
const { isReady } = formState;
8889

90+
const previousRecordId = useRef(record?.id);
91+
8992
useEffect(() => {
90-
reset(defaultValuesIncludingRecord);
91-
}, [defaultValuesIncludingRecord, reset]);
93+
const recordIdChanged = record?.id !== previousRecordId.current;
94+
previousRecordId.current = record?.id;
95+
96+
reset(
97+
defaultValuesIncludingRecord,
98+
recordIdChanged ? undefined : resetOptions
99+
);
100+
}, [defaultValuesIncludingRecord, reset, resetOptions, record?.id]);
92101

93102
// notify on invalid form
94103
useNotifyIsFormInvalid(form.control, !disableInvalidFormNotification);

0 commit comments

Comments
 (0)