最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React + Formik: Use value for nested object - Stack Overflow

programmeradmin3浏览0评论

I have the following model for my React (TypeScript) app:

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

I am using Formik and react-bootstrap to create a new ProjectInput from a Form:

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>

So in the end it should look like:

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

My problem is, that the value for the name input stays empty.

I tried to add const init = this.state.project || { name: { 'en': '' }, in my render or for my state, but this did not do anything.

I have the following model for my React (TypeScript) app:

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

I am using Formik and react-bootstrap to create a new ProjectInput from a Form:

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>

So in the end it should look like:

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

My problem is, that the value for the name input stays empty.

I tried to add const init = this.state.project || { name: { 'en': '' }, in my render or for my state, but this did not do anything.

Share Improve this question edited Dec 5, 2019 at 14:53 Vencovsky 31.7k22 gold badges130 silver badges195 bronze badges asked Dec 5, 2019 at 14:34 mrksmrks 5,63113 gold badges59 silver badges80 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 7

TL;DR

Change in your Form.Control the prop name to name.en/name.fr


First of all, initialValues is a prop that will be set and won't change unless you pass the prop enableReinitialize. So it isn't good to do this.state.project || { name: { 'en': '' } because it will only assume the first value of that, it can be this.state.project or { name: { 'en': '' }, but you will never know.

Second, to solve your problem, if you look at the docs about handleChange:

General input change event handler. This will update the values[key] where key is the event-emitting input's name attribute. If the name attribute is not present, handleChange will look for an input's id attribute. Note: "input" here means all HTML inputs.

But in your Form.Control you are passing the name attribute as name="name".

So it's trying to update name and not e.g. name.en.

You should change

   <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}
    />

To

   <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}
    />

Here is the docs that shows why you should use name.en instead of just name.

It described in docs https://formik/docs/guides/arrays

import React from 'react';
import { Formik, Form, Field } from 'formik';

export const NestedExample = () => (
  <div>
    <h1>Social Profiles</h1>
    <Formik
      initialValues={{
        social: {
          facebook: '',
          twitter: '',
        },
      }}
      onSubmit={values => {
        // same shape as initial values
        console.log(values);
      }}
    >
      <Form>
        <Field name="social.facebook" />
        <Field name="social.twitter" />
        <button type="submit">Submit</button>
      </Form>
    </Formik>
  </div>
);
发布评论

评论列表(0)

  1. 暂无评论