diff --git a/packages/ra-core/src/form/FormWithRedirect.spec.tsx b/packages/ra-core/src/form/FormWithRedirect.spec.tsx
new file mode 100644
index 00000000000..3f4db1dadde
--- /dev/null
+++ b/packages/ra-core/src/form/FormWithRedirect.spec.tsx
@@ -0,0 +1,82 @@
+import * as React from 'react';
+import { cleanup } from '@testing-library/react';
+
+import { renderWithRedux } from '../util';
+import FormWithRedirect from './FormWithRedirect';
+import useInput from './useInput';
+
+describe('FormWithRedirect', () => {
+ afterEach(cleanup);
+ const Input = props => {
+ const { input } = useInput(props);
+
+ return ;
+ };
+
+ it('Does not make the form dirty when reinitialized from a record', () => {
+ const renderProp = jest.fn(() => (
+
+ ));
+ const { getByDisplayValue, rerender } = renderWithRedux(
+
+ );
+
+ expect(renderProp.mock.calls[0][0].pristine).toEqual(true);
+ expect(getByDisplayValue('Bar')).not.toBeNull();
+
+ rerender(
+
+ );
+
+ expect(renderProp.mock.calls[1][0].pristine).toEqual(true);
+ expect(getByDisplayValue('Foo')).not.toBeNull();
+ expect(renderProp).toHaveBeenCalledTimes(2);
+ });
+
+ it('Does not make the form dirty when reinitialized from a different record', () => {
+ const renderProp = jest.fn(() => (
+
+ ));
+ const { getByDisplayValue, rerender } = renderWithRedux(
+
+ );
+
+ expect(renderProp.mock.calls[0][0].pristine).toEqual(true);
+ expect(getByDisplayValue('Foo')).not.toBeNull();
+
+ rerender(
+
+ );
+
+ expect(renderProp.mock.calls[1][0].pristine).toEqual(true);
+ expect(getByDisplayValue('Foo')).not.toBeNull();
+ expect(renderProp).toHaveBeenCalledTimes(2);
+ });
+});
diff --git a/packages/ra-core/src/form/useInitializeFormWithRecord.ts b/packages/ra-core/src/form/useInitializeFormWithRecord.ts
index 455c071b296..48ec978d115 100644
--- a/packages/ra-core/src/form/useInitializeFormWithRecord.ts
+++ b/packages/ra-core/src/form/useInitializeFormWithRecord.ts
@@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { useForm } from 'react-final-form';
-import { isObject } from '../inference/assertions';
+import { merge } from 'lodash';
/**
* Restore the record values which should override any default values specified on the form.
@@ -13,42 +13,9 @@ const useInitializeFormWithRecord = record => {
return;
}
- const registeredFields = form.getRegisteredFields();
-
- // react-final-form does not provide a way to set multiple values in one call.
- // Using batch ensure we don't get rerenders until all our values are set
- form.batch(() => {
- Object.keys(record).forEach(key => {
- // We have to check that the record key is actually registered as a field
- // as some record keys may not have a matching input
- if (registeredFields.some(field => field === key)) {
- if (Array.isArray(record[key])) {
- // array of values
- record[key].forEach((value, index) => {
- if (
- isObject(value) &&
- Object.keys(value).length > 0
- ) {
- // array of objects
- Object.keys(value).forEach(key2 => {
- form.change(
- `${key}[${index}].${key2}`,
- value[key2]
- );
- });
- } else {
- // array of scalar values
- form.change(`${key}[${index}]`, value);
- }
- });
- } else {
- // scalar value
- form.change(key, record[key]);
- }
- form.resetFieldState(key);
- }
- });
- });
+ const initialValues = form.getState().initialValues;
+ const initialValuesMergedWithRecord = merge({}, initialValues, record);
+ form.initialize(initialValuesMergedWithRecord);
}, [form, JSON.stringify(record)]); // eslint-disable-line react-hooks/exhaustive-deps
};