resetFields执行了不必要的组件销毁
Closed this issue · 5 comments
复现代码
codesandbox: https://codesandbox.io/p/sandbox/antd-resetfields-bug-ys6zp2
import React from 'react';
import { Form, Button, Space, Input } from 'antd';
export default function App() {
const [form] = Form.useForm();
return (
<div className="App">
<Form
form={form}
layout="vertical"
initialValues={{
field1: '',
field2: '',
list: [{ field3: '' }],
}}
>
<Form.Item
name="field1"
label="field1"
rules={[{ required: true, message: 'field1 is required' }]}
>
<Input />
</Form.Item>
<Form.Item noStyle shouldUpdate>
{() => {
return (
<Form.Item
name="field2"
label="field2"
rules={[{ required: true, message: 'field2 is required' }]}
>
<Input />
</Form.Item>
);
}}
</Form.Item>
<Form.List name="list">
{(fields) => {
return (
<>
{fields.map((field) => {
return (
<div key={field.key}>
<Form.Item
name={[field.name, 'field3']}
label="field3"
rules={[
{ required: true, message: 'field3 is requried' },
]}
>
<Input />
</Form.Item>
</div>
);
})}
</>
);
}}
</Form.List>
</Form>
<div>
<Space>
<Button
onClick={() => form.validateFields()}
>validateFields</Button>
<Button
onClick={() => {
form.resetFields();
form.validateFields();
}}
>resetFields and validateFields</Button>
</Space>
</div>
</div>
);
}
复现步骤
- 点击
validateFields
; - 点击
resetFields and validateFields
;
期望结果
- 点击
validateFields
后页面上出现三个报错; - 点击
resetFields and validateFields
后表单重置,并且页面上出现三个报错;
实际结果
validateFields
按钮表现和期望一致;- 点击
resetFields and validateFields
后表单重置,但是页面上只有第一个输入框出现报错;
分析原因
rc-field-form
在执行resetFields
时,将所有Field
子元素都进行了销毁重建:
因此在调用resetFields
之后,包裹在FormI.Item
或Form.List
下的元素都被销毁了,这时立即同步调用validateFields
会忽略掉这些元素的校验;
我觉得实际上当children
为function
或者name
不存在时,子元素没有必要进行销毁重建,只需要rerender
即可;
需要加一次强行刷新,但是销毁重建是需要的,因为会有副作用要考虑。一些自定义组件对受控变回 undefined
非受控处理逻辑是和 mount 就是 undefined
是不一样的
需要加一次强行刷新,但是销毁重建是需要的,因为会有副作用要考虑。一些自定义组件对受控变回
undefined
非受控处理逻辑是和 mount 就是undefined
是不一样的
只有表单组件需要销毁重建来清除副作用吧?比如:
<Form.Item
name="field"
label="field"
>
<Field />
</Form.Item>
而如果像这样的写法:
- 子元素是
function
的情况
<Form.Item
noStyle
shouldUpdate
>
{() => {
return (
<Form.Item
name="field"
label="field"
rules={[{ required: true, message: 'field is required' }]}
>
<Input />
</Form.Item>
);
}}
</Form.Item>
<Form.List name="list">
{(fields) => {
return (
<>
{fields.map((field) => {
return (
<div key={field.key}>
<Form.Item
name={[field.name, 'field3']}
label="field3"
rules={[
{ required: true, message: 'field3 is requried' },
]}
>
<Input />
</Form.Item>
</div>
);
})}
</>
);
}}
</Form.List>
Form.Item
没有配置name
,只作为样式存在
<Form.Item>
<Button>提交</Button>
</Form.Item>
以上两种情况的子元素是非表单元素,因此不需要清除副作用;
这样改是否可行:
- this.refresh();
+ if (!this.props.name || typeof this.props.children === 'function') {
+ this.reRender();
+ } else {
+ this.refresh();
+ }
refresh
会让子组件重建,什么情况下要让子组件重建?
refresh
会让子组件重建,什么情况下要让子组件重建?
目前是调用resetFields
时,所有的Form.Item
的子元素都会重建;
但我觉得只需要让受控的表单组件重建即可:配置了name
并且子元素为非function
嗯,精细化处理是最好的。但是很多业务写代码粒度比较粗,常见有来自 store 的混合数据会带有副作用。所以一把梭虽然不是最高效的,但是却是最干净的。