Skip to content

Commit 98a1235

Browse files
authored
fix: Preserve should not affect Form.List (#181)
* fix: Form preserve should not work on Form.List Item * add warning
1 parent 9a8c81c commit 98a1235

File tree

4 files changed

+86
-12
lines changed

4 files changed

+86
-12
lines changed

examples/list.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ const Demo = () => {
2121
console.log('values:', values);
2222
}}
2323
style={{ border: '1px solid red', padding: 15 }}
24+
preserve={false}
2425
>
26+
<Form.Field shouldUpdate>{() => JSON.stringify(form.getFieldsValue(), null, 2)}</Form.Field>
27+
2528
<List name="users">
2629
{(fields, { add, remove }) => {
2730
console.log('Demo Fields:', fields);

src/Field.tsx

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,13 @@ export interface InternalFieldProps {
7373
initialValue?: any;
7474
onReset?: () => void;
7575
preserve?: boolean;
76+
77+
/** @private Passed by Form.List props. Do not use since it will break by path check. */
78+
isListField?: boolean;
7679
}
7780

7881
export interface FieldProps extends Omit<InternalFieldProps, 'name'> {
7982
name?: NamePath;
80-
81-
/** @private Passed by Form.List props. */
82-
isListField?: boolean;
8383
}
8484

8585
export interface FieldState {
@@ -102,7 +102,7 @@ class Field extends React.Component<InternalFieldProps, FieldState, InternalForm
102102
resetCount: 0,
103103
};
104104

105-
private cancelRegisterFunc: (preserve?: boolean) => void | null = null;
105+
private cancelRegisterFunc: (isListField?: boolean, preserve?: boolean) => void | null = null;
106106

107107
private destroy = false;
108108

@@ -140,10 +140,10 @@ class Field extends React.Component<InternalFieldProps, FieldState, InternalForm
140140
}
141141

142142
public cancelRegister = () => {
143-
const { preserve } = this.props;
143+
const { preserve, isListField } = this.props;
144144

145145
if (this.cancelRegisterFunc) {
146-
this.cancelRegisterFunc(preserve);
146+
this.cancelRegisterFunc(isListField, preserve);
147147
}
148148
this.cancelRegisterFunc = null;
149149
};
@@ -498,13 +498,21 @@ class Field extends React.Component<InternalFieldProps, FieldState, InternalForm
498498
}
499499
}
500500

501-
const WrapperField: React.FC<FieldProps> = ({ name, isListField, ...restProps }) => {
501+
const WrapperField: React.FC<FieldProps> = ({ name, ...restProps }) => {
502502
const namePath = name !== undefined ? getNamePath(name) : undefined;
503503

504504
let key: string = 'keep';
505-
if (!isListField) {
505+
if (!restProps.isListField) {
506506
key = `_${(namePath || []).join('_')}`;
507507
}
508+
509+
if (process.env.NODE_ENV !== 'production') {
510+
warning(
511+
restProps.preserve !== false || !restProps.isListField,
512+
'`preserve` should not apply on Form.List fields.',
513+
);
514+
}
515+
508516
return <Field key={key} name={namePath} {...restProps} />;
509517
};
510518

src/useForm.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -493,12 +493,12 @@ export class FormStore {
493493
}
494494

495495
// un-register field callback
496-
return (preserve?: boolean) => {
496+
return (isListField?: boolean, preserve?: boolean) => {
497497
this.fieldEntities = this.fieldEntities.filter(item => item !== entity);
498498

499499
// Clean up store value if preserve
500500
const mergedPreserve = preserve !== undefined ? preserve : this.preserve;
501-
if (mergedPreserve === false) {
501+
if (mergedPreserve === false && !isListField) {
502502
const namePath = entity.getNamePath();
503503
if (this.getFieldValue(namePath) !== undefined) {
504504
this.store = setValue(this.store, namePath, undefined);

tests/preserve.test.tsx

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
/* eslint-disable no-template-curly-in-string */
1+
/* eslint-disable no-template-curly-in-string, arrow-body-style */
22
import React from 'react';
33
import { mount } from 'enzyme';
4-
import Form from '../src';
4+
import Form, { FormInstance } from '../src';
55
import InfoField from './common/InfoField';
66
import timeout from './common/timeout';
77

@@ -74,5 +74,68 @@ describe('Form.Preserve', () => {
7474
await matchTest(true, { keep: 233 });
7575
await matchTest(false, { keep: 233, remove: 666 });
7676
});
77+
78+
it('form perishable should not crash Form.List', async () => {
79+
let form: FormInstance;
80+
81+
const wrapper = mount(
82+
<Form
83+
initialValues={{ list: ['light', 'bamboo', 'little'] }}
84+
preserve={false}
85+
ref={instance => {
86+
form = instance;
87+
}}
88+
>
89+
<Form.List name="list">
90+
{(fields, { remove }) => {
91+
return (
92+
<div>
93+
{fields.map(field => (
94+
<Form.Field {...field}>
95+
<input />
96+
</Form.Field>
97+
))}
98+
<button
99+
type="button"
100+
onClick={() => {
101+
remove(0);
102+
}}
103+
/>
104+
</div>
105+
);
106+
}}
107+
</Form.List>
108+
</Form>,
109+
);
110+
111+
wrapper.find('button').simulate('click');
112+
wrapper.update();
113+
114+
expect(form.getFieldsValue()).toEqual({ list: ['bamboo', 'little'] });
115+
});
116+
117+
it('warning when Form.List use preserve', () => {
118+
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
119+
120+
mount(
121+
<Form initialValues={{ list: ['bamboo'] }}>
122+
<Form.List name="list">
123+
{fields =>
124+
fields.map(field => (
125+
<Form.Field {...field} preserve={false}>
126+
<input />
127+
</Form.Field>
128+
))
129+
}
130+
</Form.List>
131+
</Form>,
132+
);
133+
134+
expect(errorSpy).toHaveBeenCalledWith(
135+
'Warning: `preserve` should not apply on Form.List fields.',
136+
);
137+
138+
errorSpy.mockRestore();
139+
});
77140
});
78141
/* eslint-enable no-template-curly-in-string */

0 commit comments

Comments
 (0)