【问题标题】:React + Formik: Use value for nested objectReact + Formik:使用嵌套对象的值
【发布时间】:2019-12-05 14:34:20
【问题描述】:

我的 React (TypeScript) 应用程序有以下模型:

interface IProjectInput {
    id?: string;
    name: string | i18n;
    description: string | i18n;
}
export interface i18n {
    [key: string]: string;
}

我正在使用Formikreact-bootstrapForm 创建一个新的ProjectInput

import { i18n as I18n, ... } from 'my-models';

interface State {
    validated: boolean;
    project: IProjectInput;
}

/**
 * A Form that can can edit a project
 */
class ProjectForm extends Component<Props, State> {
    constructor(props: any) {
        super(props);
        this.state = {
            project: props.project || {
                name: {},
                description: ''
            },
            validated: false
        };
    }

    async handleSubmit(values: FormikValues, actions: FormikHelpers<IProjectInput>) {
        let project = new ProjectInput();
        project = { ...project, ...values };
        console.log("form values", values);
        // actions.setSubmitting(true);
        // try {
        //     await this.props.onSubmit(project);
        // } catch (e) { }
        // actions.setSubmitting(false);
    }

    render() {
        const { t } = this.props;
        const getCurrentLng = () => i18n.language || window.localStorage.i18nextLng || '';
        const init = this.state.project || {
            name: {},
            description: ''
        };

        return (
            <div>
                <Formik
                    // validationSchema={ProjectInputSchema}
                    enableReinitialize={false}
                    onSubmit={(values, actions) => this.handleSubmit(values, actions)}
                    initialValues={init}
                >
                    {({
                        handleSubmit,
                        handleChange,
                        handleBlur,
                        values,
                        touched,
                        errors,
                        isSubmitting,
                        setFieldTouched
                    }) => {

                        return (
                            <div className="project-form">
                                <Form noValidate onSubmit={handleSubmit}>
                                    <Form.Row>
                                        <Form.Group as={Col} md={{span: 5}} controlId="projectName">
                                            <Form.Label>
                                                {t('projectName')}
                                            </Form.Label>
                                            // Input for ENGLISH text
                                            <Form.Control
                                                type="text"
                                                name="name"
                                                value={(values['name'] as I18n).en}
                                                onChange={handleChange}
                                            />
                                            // Input for FRENCH text
                                            <Form.Control
                                                type="text"
                                                name="name"
                                                value={(values['name'] as I18n).fr}
                                                onChange={handleChange}
                                            />
                                        </Form.Group>

所以最后应该是这样的:

{
  "name": {
    "en": "yes",
    "fr": "oui"
  },
  "description" : "test",
  ...
}

我的问题是,name 输入的值保持为空。

我尝试在我的render 或我的state 中添加const init = this.state.project || { name: { 'en': '' },,但这没有任何作用。

【问题讨论】:

    标签: javascript reactjs typescript formik


    【解决方案1】:

    TL;DR

    将您的Form.Control 属性name 更改为name.en/name.fr


    首先,initialValues 是一个将被设置并且不会改变的道具,除非你通过道具enableReinitialize。所以this.state.project || { name: { 'en': '' } 不好,因为它只会假设第一个值,它可以是this.state.project{ name: { 'en': '' },但你永远不会知道。

    其次,为了解决您的问题,如果您查看有关 handleChange 的文档:

    一般输入更改事件处理程序。这将更新values[key],其中键是事件发射输入的name 属性。如果name 属性不存在,handleChange 将查找输入的id 属性。注意:这里的“输入”是指所有 HTML 输入。

    但是在您的Form.Control 中,您将name 属性作为name="name" 传递。

    所以它正在尝试更新 name 而不是例如name.en.

    你应该改变

       <Form.Control
            type="text"
            name="name"
            value={(values['name'] as I18n).en}
            onChange={handleChange}
        />
        // Input for FRENCH text
        <Form.Control
            type="text"
            name="name"
            value={(values['name'] as I18n).fr}
            onChange={handleChange}
        />
    

       <Form.Control
            type="text"
            name="name.en" // correct name
            value={(values['name'] as I18n).en}
            onChange={handleChange}
        />
        // Input for FRENCH text
        <Form.Control
            type="text"
            name="name.fr" // correct name
            value={(values['name'] as I18n).fr}
            onChange={handleChange}
        />
    

    这里是docs,它说明了为什么应该使用name.en 而不仅仅是name

    【讨论】:

    • 有时我觉得自己很愚蠢,就像现在一样......谢谢@Vencovsky 抽出时间,不仅发布了正确的答案,而且还提供了一个非常好的解释!我阅读了此文档页面,但完全忽略了namepart。
    • 我今天的最后一个问题是,我如何声明name: {} 以避免得到Warning: A component is changing an uncontrolled input of type text to be controlled. 我可以在任何地方都使用name: {'en':'','fr':''},但我不想硬编码它:) @Vencovsky跨度>
    • @mrks 您需要对其进行硬编码。如果页面上的输入是硬编码的,那么您需要对其进行硬编码。如果输入由.map 有条件地呈现或呈现,那么您应该在name: {} 中执行相同的操作。为避免changing an uncontrolled input of type text to be controlled,呈现的input 名称必须在initialValues 中。
    猜你喜欢
    • 2020-09-21
    • 2020-12-21
    • 2020-09-17
    • 1970-01-01
    • 2021-03-08
    • 2017-09-11
    • 2022-06-30
    • 2021-04-24
    • 1970-01-01
    相关资源
    最近更新 更多