diff --git a/docs/demo/list-unmount.md b/docs/demo/list-unmount.md new file mode 100644 index 00000000..b7154eec --- /dev/null +++ b/docs/demo/list-unmount.md @@ -0,0 +1,3 @@ +## list + + diff --git a/docs/examples/list-unmount.tsx b/docs/examples/list-unmount.tsx new file mode 100644 index 00000000..b62f9586 --- /dev/null +++ b/docs/examples/list-unmount.tsx @@ -0,0 +1,56 @@ +import React, { useState } from 'react'; +import Form from 'rc-field-form'; +import Input from './components/Input'; +import LabelField from './components/LabelField'; + +const Demo = () => { + const [form] = Form.useForm(); + const [isShow, setIsShow] = useState(true); + + return ( +
+
{ + console.log(JSON.stringify(values, null, 2)); + console.log(JSON.stringify(form.getFieldsValue({ strict: true }), null, 2)); + }} + initialValues={{ + users: [ + { name: 'a', age: '1' }, + { name: 'b', age: '2' }, + ], + }} + > + {() => JSON.stringify(form.getFieldsValue(), null, 2)} + + + {fields => { + return ( +
+ {fields.map(field => ( +
+ + + + {isShow && ( + + + + )} +
+ ))} +
+ ); + }} +
+ + +
+
+ ); +}; + +export default Demo; diff --git a/src/useForm.ts b/src/useForm.ts index d948c074..7135aead 100644 --- a/src/useForm.ts +++ b/src/useForm.ts @@ -910,6 +910,8 @@ export class FormStore { const namePathList: InternalNamePath[] | undefined = provideNameList ? nameList.map(getNamePath) : []; + // Same namePathList, but does not include Form.List name + const finalValueNamePathList = [...namePathList]; // Collect result in promise list const promiseList: Promise[] = []; @@ -921,9 +923,17 @@ export class FormStore { const { recursive, dirty } = options || {}; this.getFieldEntities(true).forEach((field: FieldEntity) => { + const fieldNamePath = field.getNamePath(); + // Add field if not provide `nameList` if (!provideNameList) { - namePathList.push(field.getNamePath()); + if ( + // When Form.List has a value, filter Form.List `name` + !(field.isList() && namePathList.some(name => matchNamePath(name, fieldNamePath, true))) + ) { + finalValueNamePathList.push(fieldNamePath); + } + namePathList.push(fieldNamePath); } // Skip if without rule @@ -936,7 +946,6 @@ export class FormStore { return; } - const fieldNamePath = field.getNamePath(); validateNamePathList.add(fieldNamePath.join(TMP_SPLIT)); // Add field validate rule in to promise list @@ -1000,7 +1009,7 @@ export class FormStore { const returnPromise: Promise = summaryPromise .then((): Promise => { if (this.lastValidatePromise === summaryPromise) { - return Promise.resolve(this.getFieldsValue(namePathList)); + return Promise.resolve(this.getFieldsValue(finalValueNamePathList)); } return Promise.reject([]); }) diff --git a/tests/list.test.tsx b/tests/list.test.tsx index 598eb5f5..e21a8f84 100644 --- a/tests/list.test.tsx +++ b/tests/list.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { fireEvent, render, act } from '@testing-library/react'; import { resetWarned } from '@rc-component/util/lib/warning'; import Form, { Field, List } from '../src'; @@ -937,4 +937,98 @@ describe('Form.List', () => { expect(formRef.current!.getFieldValue('list')).toEqual([{ user: '1' }, { user: '3' }]); }); + + it('list unmount', async () => { + const valueRef = React.createRef(); + + const Demo = () => { + const [isShow, setIsShow] = useState(true); + return ( +
{ + valueRef.current = values; + }} + > + + {fields => { + return fields.map(field => ( +
+ + + + {isShow && ( + + + + )} +
+ )); + }} +
+ + +
+ ); + }; + + const { queryByTestId } = render(); + fireEvent.click(queryByTestId('submit')); + await act(async () => { + await timeout(); + }); + expect(valueRef.current).toEqual({ + users: [ + { name: 'a', age: '1' }, + { name: 'b', age: '2' }, + ], + }); + + fireEvent.click(queryByTestId('hide')); + fireEvent.click(queryByTestId('submit')); + await act(async () => { + await timeout(); + }); + expect(valueRef.current).toEqual({ users: [{ name: 'a' }, { name: 'b' }] }); + }); + + it('list rules', async () => { + const onFinishFailed = jest.fn(); + + const Demo = () => { + return ( +
+ Promise.reject('error') }]}> + {fields => { + return fields.map(field => ( + + + + )); + }} + + +
+ ); + }; + + const { queryByTestId } = render(); + fireEvent.click(queryByTestId('submit')); + await act(async () => { + await timeout(); + }); + + expect(onFinishFailed).toHaveBeenCalled(); + }); });