Skip to content

Commit 66b9294

Browse files
authored
fix: Nest Field.List item will not clean up on onValuesChange when removed (#213)
* test: test driven * fix: Ignore fieldList when list field * chore: Clean up
1 parent 0e81dc0 commit 66b9294

File tree

6 files changed

+58
-8
lines changed

6 files changed

+58
-8
lines changed

src/Field.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,8 @@ class Field extends React.Component<InternalFieldProps, FieldState> implements F
359359

360360
public getErrors = () => this.errors;
361361

362+
public isListField = () => this.props.isListField;
363+
362364
// ============================= Child Component =============================
363365
public getMeta = (): Meta => {
364366
// Make error & validating in cache to save perf

src/List.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@ import FieldContext from './FieldContext';
55
import Field from './Field';
66
import { move, getNamePath } from './utils/valueUtil';
77

8-
interface ListField {
8+
export interface ListField {
99
name: number;
1010
key: number;
1111
isListField: boolean;
1212
}
1313

14-
interface ListOperations {
14+
export interface ListOperations {
1515
add: (defaultValue?: StoreValue, index?: number) => void;
1616
remove: (index: number | number[]) => void;
1717
move: (from: number, to: number) => void;
1818
}
1919

20-
interface ListProps {
20+
export interface ListProps {
2121
name: NamePath;
2222
rules?: ValidatorRule[];
2323
validateTrigger?: string | string[] | false;

src/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ RefForm.Field = Field;
2525
RefForm.List = List;
2626
RefForm.useForm = useForm;
2727

28-
export { FormInstance, Field, List, useForm, FormProvider };
28+
export { FormInstance, Field, List, useForm, FormProvider, FormProps };
2929

3030
export default RefForm;

src/interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export interface FieldEntity {
9797
isFieldTouched: () => boolean;
9898
isFieldDirty: () => boolean;
9999
isFieldValidating: () => boolean;
100+
isListField: () => boolean;
100101
validateRules: (options?: ValidateOptions) => Promise<string[]>;
101102
getMeta: () => Meta;
102103
getNamePath: () => InternalNamePath;

src/useForm.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,12 @@ export class FormStore {
211211
const namePath =
212212
'INVALIDATE_NAME_PATH' in entity ? entity.INVALIDATE_NAME_PATH : entity.getNamePath();
213213

214+
// Ignore when it's a list item and not specific the namePath,
215+
// since parent field is already take in count
216+
if (!nameList && (entity as FieldEntity).isListField?.()) {
217+
return;
218+
}
219+
214220
if (!filterFunc) {
215221
filteredNameList.push(namePath);
216222
} else {

tests/list.test.js renamed to tests/list.test.tsx

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
11
import React from 'react';
22
import { act } from 'react-dom/test-utils';
3-
import { mount } from 'enzyme';
3+
import { mount, ReactWrapper } from 'enzyme';
44
import { resetWarned } from 'rc-util/lib/warning';
5-
import Form, { Field, List } from '../src';
5+
import Form, { Field, List, FormProps } from '../src';
6+
import { ListField, ListOperations, ListProps } from '../src/List';
7+
import { Meta } from '../src/interface';
68
import { Input } from './common/InfoField';
79
import { changeValue, getField } from './common';
810
import timeout from './common/timeout';
911

1012
describe('Form.List', () => {
1113
let form;
1214

13-
function generateForm(renderList, formProps, listProps) {
15+
function generateForm(
16+
renderList?: (
17+
fields: ListField[],
18+
operations: ListOperations,
19+
meta: Meta,
20+
) => JSX.Element | React.ReactNode,
21+
formProps?: FormProps,
22+
listProps?: Partial<ListProps>,
23+
): [ReactWrapper, () => ReactWrapper] {
1424
const wrapper = mount(
1525
<div>
1626
<Form
@@ -523,7 +533,7 @@ describe('Form.List', () => {
523533
it('warning if children is not function', () => {
524534
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
525535

526-
generateForm(<div />);
536+
generateForm(<div /> as any);
527537

528538
expect(errorSpy).toHaveBeenCalledWith('Warning: Form.List only accepts function as children.');
529539

@@ -605,4 +615,35 @@ describe('Form.List', () => {
605615
expect(currentValue).toEqual([undefined]);
606616
expect(currentMeta.errors).toEqual(['Bamboo Light']);
607617
});
618+
619+
it('Nest list remove should trigger correct onValuesChange', () => {
620+
const onValuesChange = jest.fn();
621+
622+
const [wrapper] = generateForm(
623+
(fields, operation) => (
624+
<div>
625+
{fields.map(field => (
626+
<Field {...field} name={[field.name, 'first']}>
627+
<Input />
628+
</Field>
629+
))}
630+
<button
631+
type="button"
632+
onClick={() => {
633+
operation.remove(1);
634+
}}
635+
/>
636+
</div>
637+
),
638+
{
639+
onValuesChange,
640+
initialValues: {
641+
list: [{ first: 'light' }, { first: 'bamboo' }],
642+
},
643+
},
644+
);
645+
646+
wrapper.find('button').simulate('click');
647+
expect(onValuesChange).toHaveBeenCalledWith(expect.anything(), { list: [{ first: 'light' }] });
648+
});
608649
});

0 commit comments

Comments
 (0)