diff --git a/packages/ra-core/src/form/FormDataConsumer.spec.tsx b/packages/ra-core/src/form/FormDataConsumer.spec.tsx
index 991a45ee3b9..f8a9a14d57c 100644
--- a/packages/ra-core/src/form/FormDataConsumer.spec.tsx
+++ b/packages/ra-core/src/form/FormDataConsumer.spec.tsx
@@ -1,7 +1,17 @@
import * as React from 'react';
-import { render } from '@testing-library/react';
+import { render, waitFor, screen, fireEvent } from '@testing-library/react';
-import { FormDataConsumerView } from './FormDataConsumer';
+import FormDataConsumer, { FormDataConsumerView } from './FormDataConsumer';
+import { testDataProvider } from '../dataProvider';
+import {
+ AdminContext,
+ BooleanInput,
+ SimpleForm,
+ TextInput,
+ SimpleFormIterator,
+ ArrayInput,
+} from 'ra-ui-materialui';
+import expect from 'expect';
describe('FormDataConsumerView', () => {
it('does not call its children function with scopedFormData and getSource if it did not receive an index prop', () => {
@@ -26,6 +36,7 @@ describe('FormDataConsumerView', () => {
it('calls its children function with scopedFormData and getSource if it received an index prop', () => {
const children = jest.fn(({ getSource }) => {
getSource('id');
+ return null;
});
const formData = { id: 123, title: 'A title', authors: [{ id: 0 }] };
@@ -46,4 +57,109 @@ describe('FormDataConsumerView', () => {
'authors[0].id'
);
});
+
+ it('calls its children with updated formData on first render', async () => {
+ let globalFormData;
+ render(
+
+
+
+
+ {({ formData, ...rest }) => {
+ globalFormData = formData;
+
+ return ;
+ }}
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(globalFormData).toEqual({ hi: true, bye: undefined });
+ });
+ });
+
+ it('should be reactive', async () => {
+ render(
+
+
+
+
+ {({ formData, ...rest }) =>
+ !formData.hi ? (
+
+ ) : null
+ }
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(
+ screen.queryByLabelText('resources.undefined.fields.bye')
+ ).toBeNull();
+ });
+
+ fireEvent.click(screen.getByLabelText('resources.undefined.fields.hi'));
+
+ await waitFor(() => {
+ expect(
+ screen.getByLabelText('resources.undefined.fields.bye')
+ ).not.toBeNull();
+ });
+ });
+
+ it('calls its children with updated scopedFormData when inside an ArrayInput', async () => {
+ let globalScopedFormData;
+ render(
+
+
+
+
+
+
+ {({
+ formData,
+ scopedFormData,
+ getSource,
+ ...rest
+ }) => {
+ globalScopedFormData = scopedFormData;
+ return scopedFormData &&
+ scopedFormData.name ? (
+
+ ) : null;
+ }}
+
+
+
+
+
+ );
+
+ expect(globalScopedFormData).toEqual(undefined);
+
+ fireEvent.click(screen.getByLabelText('ra.action.add'));
+
+ expect(globalScopedFormData).toEqual({ name: '' });
+
+ fireEvent.change(
+ screen.getByLabelText('resources.undefined.fields.authors.name'),
+ {
+ target: { value: 'a' },
+ }
+ );
+
+ await waitFor(() => {
+ expect(globalScopedFormData).toEqual({
+ name: 'a',
+ role: undefined,
+ });
+ });
+ });
});
diff --git a/packages/ra-core/src/form/FormDataConsumer.tsx b/packages/ra-core/src/form/FormDataConsumer.tsx
index 519e3ea80ce..3f4d9668e89 100644
--- a/packages/ra-core/src/form/FormDataConsumer.tsx
+++ b/packages/ra-core/src/form/FormDataConsumer.tsx
@@ -1,6 +1,6 @@
import * as React from 'react';
import { ReactNode } from 'react';
-import { useWatch } from 'react-hook-form';
+import { useWatch, useFormContext, FieldValues } from 'react-hook-form';
import get from 'lodash/get';
import warning from '../util/warning';
@@ -43,22 +43,27 @@ import warning from '../util/warning';
* );
*/
const FormDataConsumer = (props: ConnectedProps) => {
- const formData = useWatch();
+ const { getValues } = useFormContext();
+ let formData = useWatch();
+
+ //useWatch will initially return the provided defaultValues of the form.
+ //We must get the initial formData from getValues
+ if (Object.keys(formData).length === 0) {
+ (formData as FieldValues) = getValues();
+ }
return ;
};
export const FormDataConsumerView = (props: Props) => {
const { children, form, formData, source, index, ...rest } = props;
- let scopedFormData = formData;
- let getSource;
let getSourceHasBeenCalled = false;
let ret;
// If we have an index, we are in an iterator like component (such as the SimpleFormIterator)
if (typeof index !== 'undefined' && source) {
- scopedFormData = get(formData, source);
- getSource = (scopedSource: string) => {
+ const scopedFormData = get(formData, source);
+ const getSource = (scopedSource: string) => {
getSourceHasBeenCalled = true;
return `${source}.${scopedSource}`;
};