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 };