这里发现一个 Form 组件的一个需要注意的点。
情形:父组件使用map嵌套子组件,子组件里面有表单填写的内容,使用了Form.getFieldDecorator 属性,如果嵌套的子组件只有几个,则没有问题,如果嵌套的子组件达到几十个,那么会造成页面填写数据时出现卡顿的现象。
代码如下:
<Form onSubmit={this.handleSubmit}>
<FormItem {...formItemLayout} label="设备名">
{getFieldDecorator('name', {
rules: [
{
required: true,
message: '请输入设备名',
},
],
})(<Input placeholder="请填写" />)}
</FormItem>
</Form>
来看一下 antd 对 getFieldDecorator 属性的描述
【经过 getFieldDecorator 包装的控件,表单控件会自动添加 value 、onChange(或 trigger 指定的其他属性),数据同步将被 Form 接管】
这句的意思呢,如上图代码所示,在form的组件中,用 getFieldDecorator 包装控件,就会为表单中的包装组件自动加上value 、onChange 两个属性,就像示例中的 input 组件,接下来我们就可以用 form.validateFields 属性获取到这个包装组件的值了。这句话不好理解,来看下一般情况下获取组件的代码,就好理解多了。
<FormItem label="年龄:"
<input
type="number"
value={age.value || ''}
onChange={(e) => onFormChange('age',+e.target.value)}
/>
</FormItem>
这段代码是在 state中设置了input组件的值,然后通过 onChange 组件改变 state中input组件的值,如果用了 getFieldDecorator 那就不用额外设置了。
问题出现的原因:
回到之前出现问题的情形,页面出现卡顿的问题原因是,当我们有七十个子组件时,在任意一个组件中填写数据,即使只填入一个数字,也会造成七十个子组件重新渲染。
解决方法:
当然,这里又一个解决方法,在父组件使用map生成子组件的时候,就为每个子组件设置一个唯一的id,然后使用生命周期中的 shouldComponentUpdate 做渲染判断,这样就能只渲染一个子组件。不过这个方法存在一些坑,比如对于 Modal 组件的点击后渲染,还需要做些额外的操作。如果可以的话,可以使用原生写,自定义 onChange 函数。
解决方法的部分代码:
shouldComponentUpdate (nextProps, nextState){
if(nextProps.pollinginstance.activeNum===this.props.number) {
return true;
}
return false;
}
深入解析 Form 源码:
为什么会出现只填写一个数据,整个组件就会重新渲染呢?可以看出,是由 Form 中的 onChange 函数引起的,使用React Developer Tools 可以看一下 input 这个组件,value 和 onChange 都是自动添加的。
以下是在网上找到的相关源码
// 代码片段一
onCollect(name_, action, ...args) {
const { name, field, fieldMeta } = this.onCollectCommon(name_, action, args);
const { validate } = fieldMeta;
const newField = {
...field,
dirty: hasRules(validate),
};
// setFields请查看代码片段二
this.setFields({
[name]: newField,
});
},
// 代码片段二
setFields(maybeNestedFields) {
const fields = this.fieldsStore.flattenRegisteredFields(maybeNestedFields);
this.fieldsStore.setFields(fields);
if (onFieldsChange) {
const changedFields = Object.keys(fields)
.reduce((acc, name) => set(acc, name, this.fieldsStore.getField(name)), {});
onFieldsChange(this.props, changedFields, this.fieldsStore.getNestedAllFields());
}
this.forceUpdate();
},
在代码二中可以看到,新的数据保存在 fieldsStore 里之后,调用了 this.forceUpdate() 这是强制页面刷新的函数。
所以,我目前的猜想是,Form 引起的组件重新渲染,是由于改变了props 属性的值,由于 props 值改变,shouldComponentUpdate 函数判断要重新渲染,这也就是达到了一个实时更新的效果。如果组件少,那么这种实时刷新就不会造成太大的影响,如果组件多,那么就要考虑继续用 Form 是否合适了。
参考链接:
http://ju.outofmemory.cn/entry/348216