Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix dynamic render of fields with same name #117

Merged
merged 11 commits into from
Nov 22, 2017
Merged

Conversation

afc163
Copy link
Member

@afc163 afc163 commented Nov 16, 2017

Consider this situation which is used to throw error when change this.state.useInput:

{this.state.useInput ? getFieldDecorator('name')(<input />) : null}
<span>text content</span>
{this.state.useInput ? null : getFieldDecorator('name')(<input />)}

close #100
close #53
close ant-design/ant-design#4460
close ant-design/ant-design#4367
close ant-design/ant-design#6117
close ant-design/ant-design#7302
close ant-design/ant-design#7589
close ant-design/ant-design#4478

@afc163 afc163 requested a review from benjycui November 16, 2017 13:36
@coveralls
Copy link

Coverage Status

Coverage remained the same at 92.698% when pulling 47fb573 on feat-dynamic-field-binding into 08d3c15 on master.

@coveralls
Copy link

Coverage Status

Coverage remained the same at 92.698% when pulling 20bc23c on feat-dynamic-field-binding into 08d3c15 on master.

@afc163
Copy link
Member Author

afc163 commented Nov 17, 2017

  • 还需要补充用例。

this.fieldsMeta[name] = this.fieldsMeta[name] || [];
this.fieldsMeta[name].unshift();
if (this.fieldsMeta[name].length === 0) {
delete this.fields[name];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里的问题在于,当用户切换表单组件时,是否该保留之前的表单组件的校验信息和值。

@benjycui
Copy link
Member

还有个 getFieldInstance API 的,这样虽然切换没问题,但是 getFieldInstance 时会拿到 undefined.

form/src/createBaseForm.js

Lines 290 to 291 in 08d3c15

delete this.instances[name];
delete this.cachedBind[name];

@benjycui
Copy link
Member

虽然单测没挂,但之前的单测都没有考虑动态切换组件会导致的情况。

所以补充单测时,需要梳理对已有的 API 的可能影响,然后都补充单测。之前 nested field 就是没有考虑到对已有 API 的影响才爆了那么多的问题。

@afc163
Copy link
Member Author

afc163 commented Nov 17, 2017

下面立刻就绑定了,这里应该没有修改之前的逻辑。

this.instances[name] = component;

@afc163
Copy link
Member Author

afc163 commented Nov 17, 2017

@benjycui
Copy link
Member

下面立刻就绑定了,这里应该没有修改之前的逻辑。

这个 PR 是要解决先 B mount 再 A unmount 的问题,在 A unmount 时就会删除引用了。

@afc163
Copy link
Member Author

afc163 commented Nov 21, 2017

image

无论是 react 15 还是 16,在同一次 render 过程中均是先 unmount 再 mount,顺序是触发 getFieldProps => unmount saveRef => mount saveRef.

});
this.fieldsStore.setFieldMeta(name, this.clearedFieldMetaCache[name].meta);
}
delete this.clearedFieldMetaCache[name];
Copy link
Member Author

@afc163 afc163 Nov 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

换了一个思路做,unmount 的时候留一个缓存。如果相同的 name 再次注册,则恢复现场。

@benjycui

@afc163 afc163 changed the title [WIP] Fix dynamic render of fields with same name Fix dynamic render of fields with same name Nov 22, 2017
@afc163 afc163 merged commit 50673f9 into master Nov 22, 2017
@afc163 afc163 deleted the feat-dynamic-field-binding branch November 22, 2017 03:03
@HunDunDM
Copy link
Contributor

resetFields

resetFields需要清除clearedFieldMetaCache中对应的缓存

ref

clearedFieldMetaCache缓存的获取时机不应该在ref函数中,而应该在getFieldProps函数中。
否则首次render时,getFieldProps并不会获得cache中的数据,而是在render后,ref函数中才会获得cache。而此时不触发rerender,需要等到下一次render时,才会生效。

@Skandar-Ln
Copy link

我看到在这个简单例子里,替换组件的触发顺序是先 mount 再 unmount @afc163
https://codepen.io/Skandar/pen/WMRmgm

@jaredleechn
Copy link
Member

这个修改可以同步到 form@1.x 吗?在用 antd@2,一些动态 fields 的值在提交时无法获取,很头痛

@zackshen
Copy link

@jaredleechn 同求

@sun-slaven
Copy link

+1

@litcb
Copy link

litcb commented Nov 20, 2018

@jaredleechn @zackshen @sun-slaven

刚刚试了一下,将本地rc-form更新到2.2.6,然后改下使用form的API即可使用,暂时没有其他异常。不知道测试后会不会有问题。先说下方法
1、npm install rc-form --save
2、createForm() 替换antd中的Form.create()
3、Form.create(opt)中的opt.mapPropsToFields方法中,返回值需要由createFormField包裹一层
4、其他内容无需变更,包括antd引入的Form、FormItem。
import { createForm,createFormField } from 'rc-form';

mapPropsToFields: props => {
//把redux中的值取出来赋给表单,包含多个字段的值
let editFields = props.$$state.toJS().editFields;
let value = {}; //承载包裹后的结果值
Object.keys(editFields).forEach((key) => {
value[key] = createFormField(editFields[key]); //对每个字段进行包裹
})
return {
...value
};
},

直接使用createForm会丢失antd封装的必输(*号)及校验显示,所以第二条不需要改

@HSunboy
Copy link

HSunboy commented Apr 20, 2020

@jaredleechn 我在1.4.x修复了这个问题,但是采用了和官方不一样的解决方案。以下是antd 2.x的替换步骤。

  1. 安装新的包
npm i  my-rc-form
  1. 新建一个文件,导出一个create方法
import createDOMForm from 'my-rc-form/lib/createDOMForm';
import * as React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import { FormCreateOption, ComponentDecorator } from 'antd/lib/form/Form';
import { FIELD_META_PROP } from 'antd/lib/form/constants';
import warning from 'antd/lib/_util/warning';

export const create = function <TOwnProps> (options: FormCreateOption<TOwnProps> = {}): ComponentDecorator<TOwnProps> {
    const formWrapper = createDOMForm({
        fieldNameProp: 'id',
        ...options,
        fieldMetaProp: FIELD_META_PROP
    });

    /* eslint-disable react/prefer-es6-class */
    return (Component) => formWrapper(createReactClass({
        propTypes: {
            form: PropTypes.object.isRequired
        },
        childContextTypes: {
            form: PropTypes.object.isRequired
        },
        getChildContext () {
            return {
                form: this.props.form
            };
        },
        UNSAFE_componentWillMount () {
            this.__getFieldProps = this.props.form.getFieldProps;
        },
        deprecatedGetFieldProps (name, option) {
            warning(
                false,
                '`getFieldProps` is not recommended, please use `getFieldDecorator` instead, ' +
                'see: https://u.ant.design/get-field-decorator'
            );
            return this.__getFieldProps(name, option);
        },
        render () {
            this.props.form.getFieldProps = this.deprecatedGetFieldProps;

            const withRef: any = {};
            if (options.withRef) {
                withRef.ref = 'formWrappedComponent';
            } else if (this.props.wrappedComponentRef) {
                withRef.ref = this.props.wrappedComponentRef;
            }
            return <Component {...this.props} {...withRef} />;
        }
    }));
};
  1. 替换原有Form.create
import { create } from '之前定义的文件地址'

// 用create替换Form.create
create......

注意: 由于各种原因,导致 rc-form 的一系列 bug 变成了 feature。所以,稳妥起见,建议在动态表单出现错误的场景再去使用自定义的 create 方法,原有代码还是保持 Form.create

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment