如何在循环中呈现 React Final 表单字段

How to render React Final form fields in a loop

我正在尝试在循环中呈现多个 React Final Form 字段。我有一个对象数组,如下所示:

export const check_groups = [
    {
        name: "Mechanics",
        checks: [
            {typeName: "Boolean", title: "Clutch", icon: "fa fa-tire-rugged", key: "clutch", mandatory: true}
        ]
    },
    {
        name: "Maintenance & control",
        checks: [
            {typeName: "Boolean", title: "Recent timing belt", icon: "fal fa-cogs", key: "recent_timing_belt", mandatory: false},
            {typeName: "Boolean", title: "Recent service", icon: "fal fa-oil-can", key: "recent_service", mandatory: false}
        ]
    }
];

并且我想为该数组中的每个对象呈现一个字段。因此,我使用 lodash map 函数映射数组并渲染一个新组件,如下所示:

return check_groups.map(group => {
                return <div key={UUID.v4()}>
                    <div className="text-base w-full flex flex-row mb-1">
                        <div className="p-1 text-blue-dark">{group.name}</div>
                    </div>
                    {map(group.checks, (check) => {
                        switch (check.typeName) {
                            case "RegEx":
                                break;
                            case "Numeric":
                                break;
                            case "Boolean":
                                const name = `checks[${check.title}]`;
                                return (
                                    <TriStateItemField key={check.title} name={name} label={check.title} icon={check.icon} values={values}/>
                                );

                            default:
                                break;
                        }
                    })}
                </div>
            })

TriStateItemField 只是一个呈现表单字段并将数据传递给演示组件的组件 (TriStateItem):

<Field name={name}
               validate={(validators) ? composeValidators(...validators) : false}
               data-tip={dataTip}
               data-for={dataFor}>

            {({input, meta}) => {
                return (
                    <TriStateItem label={label}
                                  name={name}
                                  type={type}
                                  className={className}
                                  style={style}
                                  hasError={meta.touched && meta.error}
                                  error={meta.error}
                                  placeholder={placeholder}
                                  disabled={disabled}
                                  helperText={helperText}
                                  icon={icon}
                                  {...input}
                                  {...meta}

                    />
                )
            }}
        </Field>

TriStateItem是显示数据的组件:

class TriStateItem extends Component {
    renderValue(value){
        let new_value = undefined;
        if (value === "") new_value = true;
        if (value !== "" && value) new_value = false;
        if (value !== "" && !value) new_value = undefined;
        return new_value;
    }

    render() {
        const {label, icon, className, dataTip, dataFor, onChange, disabled, value, hasError, valid} = this.props;
        const isValid = value !== "";
        return (
            <div
                className={`flex flex-row justify-between w-full hover:bg-blue-200 bg-gray-100 cursor-pointer p-2 border-r-2 border-solid ${valid ? "border-transparent" : "border-red-500"} ${isValid ? "border-green-500" : ""} ${hasError ? "bg-red-100" : "bg-transparent" } ${className} ${disabled ? "opacity-60" : ""}`}
                onClick={() => onChange(this.renderValue(value))}>
                <div className="flex flex-row">
                    {icon !== undefined && <div className="pr-2"><i className={`text-gray-500 ${icon}`}/></div>}
                    <div>{label}</div>
                </div>
                {value === "" && <i className="fal fa-circle text-gray-500" style={{fontSize: 16}}/>}
                {value !== "" && value && <i className="fal fa-check text-green-500" style={{fontSize: 16}}/>}
                {value !== "" && !value && <i className="fal fa-times text-red-600" style={{fontSize: 16}}/>}
            </div>
        )
    }

问题是我得到如下错误:

Uncaught Invariant Violation: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

然后:

The above error occurred in the <Field> component:
    in Field (at TriStateItemField.js:8)

这是 sandbox 上的示例。如果您打开控制台,您将看到相同的错误。

知道我哪里做错了吗?

answered this on Github.

I think that UUID library (I've never used it) is generating a new key for your div on every render, which causes infinite rendering because it's unregistering all your fields and reregistering them, which requires the whole form to rerender, which generates new keys, which unregisters/reregisters all your fields, which......

Changing index.js:165 to <div key={group.name}> fixes it.

回答原问题,你可能想看看FieldArrayhttps://codesandbox.io/s/kx8qv67nk5?file=/index.js:1510-1526

我在 React Final Form 示例中找到了这个:"Field Array"