使用嵌套对象动态映射 initialValues 属性

IT技术 reactjs redux-form
2021-05-28 12:16:54

我正在尝试映射 redux-form 的initialValues属性,以便可以预填充表单(这是从外部 API 填充的用户配置文件编辑屏幕)。

因为允许用户管理哪些字段的验证发生在 API 上,所以 React JS 前端在填充初始值时需要尽可能动态。确实在 SO 上找到了另一个问题,该问题解决了动态预填充initialValues在 Redux 表单中动态加载初始值),但是该解决方案仅适用于没有嵌套的对象。不幸的是,我对 API 的响应确实包含一些嵌套:

{
    "id": 1,
    "email": "admin@test.com",
    "location_id": 2,
    "created_at": "2017-09-08 19:55:01",
    "updated_at": "2017-09-08 19:55:01",
    "can_edit": [ // <-- this array contains all the fields that the given user is allowed to edit on the given profile.
        "profile_img", // <-- possibly this could be rewritten to return for example 'profile.profile_img' rather than just 'profile_img', but not sure how that could help
        "password" // <-- not returned in the actual user object for obvious reasons, but there is logic for displaying the password field anyway
    ],
    "profile": {
        "user_id": 1,
        "first_name": "Admin",
        "last_name": "Test",
        "profile_img": "https://pbs.twimg.com/profile_images/449750767281254400/M_ukevnA.jpeg",
        "position": "Admin",
        "created_at": "2017-09-13 11:38:49",
        "updated_at": "2017-09-13 11:38:49",
    },
    "location": {
        "id": 2,
        "text": "Test Location",
        "lang": "en",
        "created_at": "2017-09-13 11:35:41",
        "updated_at": "2017-09-13 11:35:41",
        "deleted_at": null
    }
}

所以无法简单地填入initialValues直接-因为,例如,可能的可编辑的字段包括emaillocation_id根用户的对象,或者first_namelast_nameprofile_img嵌套轮廓对象的属性。

作为参考,我的 EditProfile 组件如下(截断到相关部分)。

class EditProfile extends Component {

    /**
     * Static property types.
     */
    static propTypes = {
        ...
    }

    /**
     * Renders editable profile fields. 
     */
    profileFields(user) {
        if(!this.state.currentField.length){
            this.state.currentField = user.can_edit[0]
        }

        let fields = user.can_edit.map((can_edit) => {
            switch(can_edit){

                // render an email input
                case 'email':
                    return (
                        <ProfileFieldGroup key={can_edit} show={ this.state.currentField == can_edit }>
                            <label htmlFor="email">Email</label>
                            <Field
                                name="email"
                                id="email"
                                type="email"
                                component="input"
                                value={ user.email }
                            />
                        </ProfileFieldGroup>
                    )

                // render a group of password inputs
                case 'password':
                    return (
                        <ProfileFieldGroup key={can_edit} show={ this.state.currentField == can_edit }>
                            <label htmlFor="old_password">Old Password</label>
                            <Field
                                name="old_password"
                                id="old_password"
                                type="password"
                                component="input"
                            />

                            <label htmlFor="password">New Password</label>
                            <Field
                                name="password"
                                id="password"
                                type="password"
                                component="input"
                            />

                            <label htmlFor="password_confirm">Confirm New Password</label>
                            <Field
                                name="password_confirm"
                                id="password_confirm"
                                type="password"
                                component="input"
                            />
                        </ProfileFieldGroup>
                    )

                // by default, create a text input whose ID and name match the given property
                default:
                    console.log(user.profile[can_edit])
                    return (
                        <ProfileFieldGroup key={can_edit} show={ this.state.currentField == can_edit }>
                            <label htmlFor={can_edit}>{ humanizeText(can_edit) }</label>
                            <Field
                                name={ can_edit }
                                id={ can_edit }
                                type="text"
                                component="input"
                                value={ user.profile[can_edit] }
                            />
                        </ProfileFieldGroup>
                    )
            }
        })

        return (
            <div>
                { fields }
            </div>
        )
    }

    /**
     * Handles the submit event.
     * This function is mainly managed by Redux.
     */
    submit = (values) => {
        ...
    }

    /**
     * Renders the component.
     */
    render() {
        const {
            handleSubmit,
            user: user,
            save: save
        } = this.props

        return (
            <section>
                <header>
                    <h1>Edit User</h1>
                </header>

                <div className="main">
                    <form onSubmit={ handleSubmit(this.submit) }>

                        {user.successful && !user.requesting &&
                            this.profileFields(user.user)
                        }

                        {user.requesting && !user.successful && (
                            <img className="loader" src=""/>
                        )}

                    </form>
                </div>

                <footer>

                </footer>
            </section>
        )
    }
}

然后连接到 redux:

const mapStateToProps = state => ({
    user: state.user,
    save: state.save,
    initialValues: state.user.user // <-- this right here
})

const connected = connect(mapStateToProps, { apiGetRequest, 
apiPostRequest })(EditProfile)

const formed = reduxForm({
    form: 'editprofile',
})(connected)

总结:如何从潜在的嵌套对象动态填充 redux 表单的 initialValues 属性?这样的事情实际上可能吗?

1个回答

如何从潜在的嵌套对象动态填充 redux 表单的 initialValues 属性?这样的事情实际上可能吗?

是的,这是可能的。如果您从 API 接收嵌套数据,根据文档,您可以在nameprop 中设置 keypath

function Form ({ handleSubmit }) {
  return (
    <form onSubmit={handleSubmit}>
      // ...
      <Field name="my.nested.prop" ... />
    </form>
  )
}