Skip to content

Commit ce911e0

Browse files
crazyairzombieJ
andauthored
feat: support clearOnDestroy (#692)
* feat: form destory reset stroe * feat: test * feat: clearStoreOnDestroy * feat: clearStoreOnDestroy * feat: clearStoreOnDestroy * feat: add test * feat: clearOnDestroy * feat: 移除依赖 * feat: add test * feat: add test * feat: test * feat: test * test: fill more test * chore: fix miss type * refactor: skip if no need --------- Co-authored-by: 二货机器人 <[email protected]>
1 parent 45c32d5 commit ce911e0

File tree

6 files changed

+127
-13
lines changed

6 files changed

+127
-13
lines changed

docs/demo/clearOnDestroy.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## clearOnDestroy
2+
3+
<code src="../examples/clearOnDestroy.tsx"></code>

docs/examples/clearOnDestroy.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import Form, { Field } from 'rc-field-form';
2+
import React, { useState } from 'react';
3+
import Input from './components/Input';
4+
5+
export default () => {
6+
const [load, setLoad] = useState(false);
7+
const [count, setCount] = useState(0);
8+
9+
const [form] = Form.useForm(undefined);
10+
11+
return (
12+
<>
13+
<button
14+
onClick={() => {
15+
setCount(c => c + 1);
16+
setLoad(c => !c);
17+
}}
18+
>
19+
load
20+
</button>
21+
22+
<button
23+
onClick={() => {
24+
console.log(form.getFieldsValue(true));
25+
}}
26+
>
27+
values
28+
</button>
29+
{load && (
30+
<Form form={form} initialValues={{ count }} clearOnDestroy>
31+
<Field name="count">
32+
<Input placeholder="count" />
33+
</Field>
34+
<button type="submit">Submit</button>
35+
</Form>
36+
)}
37+
</>
38+
);
39+
};

src/Form.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export interface FormProps<Values = any> extends BaseFormProps {
3333
onFinishFailed?: Callbacks<Values>['onFinishFailed'];
3434
validateTrigger?: string | string[] | false;
3535
preserve?: boolean;
36+
clearOnDestroy?: boolean;
3637
}
3738

3839
const Form: React.ForwardRefRenderFunction<FormInstance, FormProps> = (
@@ -50,6 +51,7 @@ const Form: React.ForwardRefRenderFunction<FormInstance, FormProps> = (
5051
onFieldsChange,
5152
onFinish,
5253
onFinishFailed,
54+
clearOnDestroy,
5355
...restProps
5456
}: FormProps,
5557
ref,
@@ -112,7 +114,7 @@ const Form: React.ForwardRefRenderFunction<FormInstance, FormProps> = (
112114
}
113115

114116
React.useEffect(
115-
() => destroyForm,
117+
() => () => destroyForm(clearOnDestroy),
116118
// eslint-disable-next-line react-hooks/exhaustive-deps
117119
[],
118120
);

src/interface.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ export interface InternalHooks {
225225
registerField: (entity: FieldEntity) => () => void;
226226
useSubscribe: (subscribable: boolean) => void;
227227
setInitialValues: (values: Store, init: boolean) => void;
228-
destroyForm: () => void;
228+
destroyForm: (clearOnDestroy?: boolean) => void;
229229
setCallbacks: (callbacks: Callbacks) => void;
230230
registerWatch: (callback: WatchCallBack) => () => void;
231231
getFields: (namePathList?: InternalNamePath[]) => FieldData[];
@@ -240,8 +240,8 @@ type RecursivePartial<T> = NonNullable<T> extends object
240240
[P in keyof T]?: NonNullable<T[P]> extends (infer U)[]
241241
? RecursivePartial<U>[]
242242
: NonNullable<T[P]> extends object
243-
? RecursivePartial<T[P]>
244-
: T[P];
243+
? RecursivePartial<T[P]>
244+
: T[P];
245245
}
246246
: T;
247247

src/useForm.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -156,15 +156,20 @@ export class FormStore {
156156
}
157157
};
158158

159-
private destroyForm = () => {
160-
const prevWithoutPreserves = new NameMap<boolean>();
161-
this.getFieldEntities(true).forEach(entity => {
162-
if (!this.isMergedPreserve(entity.isPreserve())) {
163-
prevWithoutPreserves.set(entity.getNamePath(), true);
164-
}
165-
});
166-
167-
this.prevWithoutPreserves = prevWithoutPreserves;
159+
private destroyForm = (clearOnDestroy?: boolean) => {
160+
if (clearOnDestroy) {
161+
// destroy form reset store
162+
this.updateStore({});
163+
} else {
164+
// Fill preserve fields
165+
const prevWithoutPreserves = new NameMap<boolean>();
166+
this.getFieldEntities(true).forEach(entity => {
167+
if (!this.isMergedPreserve(entity.isPreserve())) {
168+
prevWithoutPreserves.set(entity.getNamePath(), true);
169+
}
170+
});
171+
this.prevWithoutPreserves = prevWithoutPreserves;
172+
}
168173
};
169174

170175
private getInitialValue = (namePath: InternalNamePath) => {

tests/clearOnDestroy.test.tsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { fireEvent, render } from '@testing-library/react';
2+
import React, { useState } from 'react';
3+
import Form, { Field, type FormInstance } from '../src';
4+
import { changeValue, getInput } from './common';
5+
import { Input } from './common/InfoField';
6+
7+
describe('Form.clearOnDestroy', () => {
8+
it('works', async () => {
9+
let formCache: FormInstance | undefined;
10+
const Demo = ({ load }: { load?: boolean }) => {
11+
const [form] = Form.useForm();
12+
formCache = form;
13+
14+
return (
15+
<>
16+
{load && (
17+
<Form form={form} initialValues={{ count: '1' }} clearOnDestroy>
18+
<Field name="count">
19+
<Input />
20+
</Field>
21+
</Form>
22+
)}
23+
</>
24+
);
25+
};
26+
const { rerender } = render(<Demo load />);
27+
expect(formCache.getFieldsValue(true)).toEqual({ count: '1' });
28+
rerender(<Demo />);
29+
expect(formCache.getFieldsValue(true)).toEqual({});
30+
31+
// Rerender back should filled again
32+
rerender(<Demo load />);
33+
expect(formCache.getFieldsValue(true)).toEqual({ count: '1' });
34+
});
35+
36+
it('change value', async () => {
37+
let formCache: FormInstance | undefined;
38+
const Demo = () => {
39+
const [load, setLoad] = useState(true);
40+
41+
const [form] = Form.useForm();
42+
formCache = form;
43+
44+
return (
45+
<>
46+
<button onClick={() => setLoad(c => !c)}>load</button>
47+
{load && (
48+
<Form form={form} clearOnDestroy>
49+
<Field name="count">
50+
<Input />
51+
</Field>
52+
</Form>
53+
)}
54+
</>
55+
);
56+
};
57+
const { container, queryByText } = render(<Demo />);
58+
await changeValue(getInput(container), 'bamboo');
59+
expect(formCache.getFieldsValue(true)).toEqual({ count: 'bamboo' });
60+
fireEvent.click(queryByText('load'));
61+
expect(formCache.getFieldsValue(true)).toEqual({});
62+
formCache.setFields([{ name: 'count', value: '1' }]);
63+
expect(formCache.getFieldsValue(true)).toEqual({ count: '1' });
64+
});
65+
});

0 commit comments

Comments
 (0)