Skip to content

Commit 52e4c18

Browse files
authored
feat: support perishable (#151)
* feat: support perishable * fix compile * adjust name * doc update * add version info
1 parent 3457cc0 commit 52e4c18

File tree

8 files changed

+108
-6
lines changed

8 files changed

+108
-6
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ We use typescript to create the Type definition. You can view directly in IDE. B
6666
| form | Set form instance created by `useForm` | [FormInstance](#useform) | `Form.useForm()` |
6767
| initialValues | Initial value of Form | Object | - |
6868
| name | Config name with [FormProvider](#formprovider) | string | - |
69+
| preserve | preserve value when field removed | boolean | false |
6970
| validateMessages | Set validate message template | [ValidateMessages](#validatemessages) | - |
7071
| onFieldsChange | Trigger when any value of Field changed | (changedFields, allFields): void | - |
7172
| onFinish | Trigger when form submit and success | (values): void | - |
@@ -82,6 +83,7 @@ We use typescript to create the Type definition. You can view directly in IDE. B
8283
| initialValue | Field initial value | any | - |
8384
| name | Field name path | [NamePath](#namepath) | - |
8485
| normalize | Normalize value before update | (value, prevValue, prevValues) => any | - |
86+
| preserve | preserve value when field removed | boolean | false |
8587
| rules | Validate rules | [Rule](#rule)[] | - |
8688
| shouldUpdate | Check if Field should update | true \| (prevValues, nextValues): boolean | - |
8789
| trigger | Collect value update by event trigger | string | onChange |
@@ -267,9 +269,9 @@ async function() {
267269

268270
**Notice: Now if your validator return an `Error(message)`, not need to get error by `e => e.message`. FieldForm will handle this.**
269271

270-
## 🔥 `preserve` is no need anymore
272+
## 🔥 `preserve` is default to false
271273

272-
In `rc-form` you should use `preserve` to keep a value cause Form will auto remove a value from Field removed. Field Form will always keep the value in the Form whatever Field removed.
274+
In `rc-form` you should use `preserve` to keep a value cause Form will auto remove a value from Field removed. Field Form will always keep the value in the Form whatever Field removed. But you can still use `preserve=false` to disable value keeping since `1.5.0`.
273275

274276
## 🔥 `setFields` not trigger `onFieldsChange` and `setFieldsValue` not trigger `onValuesChange`
275277

src/Field.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export interface InternalFieldProps {
7272
messageVariables?: Record<string, string>;
7373
initialValue?: any;
7474
onReset?: () => void;
75+
preserve?: boolean;
7576
}
7677

7778
export interface FieldProps extends Omit<InternalFieldProps, 'name'> {
@@ -101,7 +102,7 @@ class Field extends React.Component<InternalFieldProps, FieldState, InternalForm
101102
resetCount: 0,
102103
};
103104

104-
private cancelRegisterFunc: () => void | null = null;
105+
private cancelRegisterFunc: (preserve?: boolean) => void | null = null;
105106

106107
private destroy = false;
107108

@@ -139,8 +140,10 @@ class Field extends React.Component<InternalFieldProps, FieldState, InternalForm
139140
}
140141

141142
public cancelRegister = () => {
143+
const { preserve } = this.props;
144+
142145
if (this.cancelRegisterFunc) {
143-
this.cancelRegisterFunc();
146+
this.cancelRegisterFunc(preserve);
144147
}
145148
this.cancelRegisterFunc = null;
146149
};

src/FieldContext.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const Context = React.createContext<InternalFormInstance>({
3535
setCallbacks: warningFunc,
3636
getFields: warningFunc,
3737
setValidateMessages: warningFunc,
38+
setPreserve: warningFunc,
3839
};
3940
},
4041
});

src/Form.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export interface FormProps extends BaseFormProps {
3030
onFinish?: Callbacks['onFinish'];
3131
onFinishFailed?: Callbacks['onFinishFailed'];
3232
validateTrigger?: string | string[] | false;
33+
preserve?: boolean;
3334
}
3435

3536
const Form: React.ForwardRefRenderFunction<FormInstance, FormProps> = (
@@ -38,6 +39,7 @@ const Form: React.ForwardRefRenderFunction<FormInstance, FormProps> = (
3839
initialValues,
3940
fields,
4041
form,
42+
preserve,
4143
children,
4244
component: Component = 'form',
4345
validateMessages,
@@ -60,6 +62,7 @@ const Form: React.ForwardRefRenderFunction<FormInstance, FormProps> = (
6062
setInitialValues,
6163
setCallbacks,
6264
setValidateMessages,
65+
setPreserve,
6366
} = (formInstance as InternalFormInstance).getInternalHooks(HOOK_MARK);
6467

6568
// Pass ref with form instance
@@ -96,6 +99,7 @@ const Form: React.ForwardRefRenderFunction<FormInstance, FormProps> = (
9699
},
97100
onFinishFailed,
98101
});
102+
setPreserve(preserve);
99103

100104
// Set initial value, init store value when first mount
101105
const mountRef = React.useRef(null);

src/interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ export interface InternalHooks {
174174
setCallbacks: (callbacks: Callbacks) => void;
175175
getFields: (namePathList?: InternalNamePath[]) => FieldData[];
176176
setValidateMessages: (validateMessages: ValidateMessages) => void;
177+
setPreserve: (preserve?: boolean) => void;
177178
}
178179

179180
export interface FormInstance {

src/useForm.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ export class FormStore {
6767

6868
private validateMessages: ValidateMessages = null;
6969

70+
private preserve?: boolean = null;
71+
7072
private lastValidatePromise: Promise<FieldError[]> = null;
7173

7274
constructor(forceRootUpdate: () => void) {
@@ -104,6 +106,7 @@ export class FormStore {
104106
setCallbacks: this.setCallbacks,
105107
setValidateMessages: this.setValidateMessages,
106108
getFields: this.getFields,
109+
setPreserve: this.setPreserve,
107110
};
108111
}
109112

@@ -135,6 +138,10 @@ export class FormStore {
135138
this.validateMessages = validateMessages;
136139
};
137140

141+
private setPreserve = (preserve?: boolean) => {
142+
this.preserve = preserve;
143+
};
144+
138145
// ========================== Dev Warning =========================
139146
private timeoutId: number = null;
140147

@@ -486,8 +493,14 @@ export class FormStore {
486493
}
487494

488495
// un-register field callback
489-
return () => {
496+
return (preserve?: boolean) => {
490497
this.fieldEntities = this.fieldEntities.filter(item => item !== entity);
498+
499+
// Clean up store value if preserve
500+
const mergedPreserve = preserve !== undefined ? preserve : this.preserve;
501+
if (mergedPreserve === false) {
502+
this.store = setValue(this.store, entity.getNamePath(), undefined);
503+
}
491504
};
492505
};
493506

tests/common/InfoField.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Field } from '../../src';
33
import { FieldProps } from '../../src/Field';
44

55
interface InfoFieldProps extends FieldProps {
6-
children: ReactElement;
6+
children?: ReactElement;
77
}
88

99
export const Input = ({ value = '', ...props }) => <input {...props} value={value} />;

tests/preserve.test.tsx

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/* eslint-disable no-template-curly-in-string */
2+
import React from 'react';
3+
import { mount } from 'enzyme';
4+
import Form from '../src';
5+
import InfoField from './common/InfoField';
6+
import timeout from './common/timeout';
7+
8+
describe('Form.Preserve', () => {
9+
const Demo = ({
10+
removeField,
11+
formPreserve,
12+
fieldPreserve,
13+
onFinish,
14+
}: {
15+
removeField: boolean;
16+
formPreserve?: boolean;
17+
fieldPreserve?: boolean;
18+
onFinish: (values: object) => void;
19+
}) => (
20+
<Form onFinish={onFinish} initialValues={{ keep: 233, remove: 666 }} preserve={formPreserve}>
21+
<InfoField name="keep" />
22+
{!removeField && <InfoField name="remove" preserve={fieldPreserve} />}
23+
</Form>
24+
);
25+
26+
it('field', async () => {
27+
const onFinish = jest.fn();
28+
const wrapper = mount(<Demo removeField={false} onFinish={onFinish} fieldPreserve={false} />);
29+
30+
async function matchTest(removeField: boolean, match: object) {
31+
onFinish.mockReset();
32+
wrapper.setProps({ removeField });
33+
wrapper.find('form').simulate('submit');
34+
await timeout();
35+
expect(onFinish).toHaveBeenCalledWith(match);
36+
}
37+
38+
await matchTest(false, { keep: 233, remove: 666 });
39+
await matchTest(true, { keep: 233 });
40+
await matchTest(false, { keep: 233 });
41+
});
42+
43+
it('form', async () => {
44+
const onFinish = jest.fn();
45+
const wrapper = mount(<Demo removeField={false} onFinish={onFinish} formPreserve={false} />);
46+
47+
async function matchTest(removeField: boolean, match: object) {
48+
onFinish.mockReset();
49+
wrapper.setProps({ removeField });
50+
wrapper.find('form').simulate('submit');
51+
await timeout();
52+
expect(onFinish).toHaveBeenCalledWith(match);
53+
}
54+
55+
await matchTest(false, { keep: 233, remove: 666 });
56+
await matchTest(true, { keep: 233 });
57+
await matchTest(false, { keep: 233 });
58+
});
59+
60+
it('form perishable but field !perishable', async () => {
61+
const onFinish = jest.fn();
62+
const wrapper = mount(
63+
<Demo removeField={false} onFinish={onFinish} formPreserve={false} fieldPreserve />,
64+
);
65+
66+
async function matchTest(removeField: boolean, match: object) {
67+
onFinish.mockReset();
68+
wrapper.setProps({ removeField });
69+
wrapper.find('form').simulate('submit');
70+
await timeout();
71+
expect(onFinish).toHaveBeenCalledWith(match);
72+
}
73+
74+
await matchTest(true, { keep: 233 });
75+
await matchTest(false, { keep: 233, remove: 666 });
76+
});
77+
});
78+
/* eslint-enable no-template-curly-in-string */

0 commit comments

Comments
 (0)