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

javascript - Edit an entry with Formik and Yup doesn't recognize the existing fields - Stack Overflow

programmeradmin3浏览0评论

I'm using Formik and Yup when I create an entity (pany in this case) in order to check if the fields are correct and all of them are introduced (all are required).

When I create an entity it works fine: it only lets you create it if all fields are introduced and the rules are fulfilled (only one rule at the moment for the email).

This is the code which works to create a new pany with 2 fields name and email:

    import React from 'react';
    import { Redirect } from 'react-router-dom';
    import { Formik, Form, Field } from 'formik';
    import { Input, Button, Label, Grid } from 'semantic-ui-react';
    import { connect } from 'react-redux';
    import * as Yup from 'yup';
    import { Creators } from '../../../actions';
    import Layout from '../../Layout/Layout';
    
    class CreateCompanyForm extends React.PureComponent {
      constructor(props) {
        super(props);
    
        this.state = {
          name: '',
          contactMail: '',
          redirectCreate: false,
          redirectEdit: false,
          edit: false,
        };
      }
    
      ponentDidMount() {
        const {
          getCompany,
          location: { pathname },
        } = this.props;
      }
    
      handleSubmit = values => {
        const { createCompany, getCompanies } = this.props;
        createCompany(values);
        this.setState({ redirectCreate: true });
        getCompanies(this.props.query);
      };
    
      render() {
        let title = 'Create Company';
        let buttonName = 'Create';
        let submit = this.handleSubmitCreate;
    
        const { redirectCreate, redirectEdit } = this.state;
    
        if (redirectCreate) {
          return <Redirect to="/panies" />;
        }
        const initialValues = {
          name: '',
          contactMail: '',
        };
        const requiredErrorMessage = 'This field is required';
        const emailErrorMessage = 'Please enter a valid email address';
        const validationSchema = Yup.object({
          name: Yup.string().required(requiredErrorMessage),
          contactMail: Yup.string()
            .email(emailErrorMessage)
            .required(requiredErrorMessage),
        });
        return (
          <Layout>
            <div>
              <Button type="submit" form="amazing">
                Create pany
              </Button>
              <Button onClick={() => this.props.history.goBack()}>Discard</Button>
              <div>Create pany</div>
            </div>
    
            <Formik
              htmlFor="amazing"
              initialValues={initialValues}
              validationSchema={validationSchema}
              onSubmit={values => this.handleSubmit(values)}>
              {({ values, errors, touched, setValues }) => (
                <Form id="amazing">
                  <Grid columns={2}>
                    <Grid.Column>
                      <Label>Company Name</Label>
                      <Field name="name" as={Input} placeholder="Hello" />
                      <div>{touched.name && errors.name ? errors.name : null}</div>
                    </Grid.Column>
    
                    <Grid.Column>
                      <Label>Contact Mail</Label>
                      <Field
                        name="contactMail"
                        as={Input}
                        placeholder="[email protected]"
                      />
                      <div>
                        {touched.contactMail && errors.contactMail
                          ? errors.contactMail
                          : null}
                      </div>
                    </Grid.Column>
                  </Grid>
                </Form>
              )}
            </Formik>
          </Layout>
        );
      }
    }
    
    const mapStateToProps = state => ({
      panies: statepaniespanies,
      pany: statepanies.selectedCompany,
      query: statepanies.query,
    });
    
    const mapDispatchToProps = {
      getCompanies: Creators.getCompaniesRequest,
      createCompany: Creators.createCompanyRequest,
      getCompany: Creators.getCompanyRequest,
      updateCompany: Creators.updateCompanyRequest,
    };

export default connect(mapStateToProps, mapDispatchToProps)(CreateCompanyForm);

The problem appears when I want to edit the pany. So when someone click on a pany on edit button it should open the pany with all its fields containing the current values which should be editable.

To get those current values I'm using the state, for example the email can be accessed from this.state.email and in order to change the value it was added onChange method.

The values can be modified in the text input. However, it triggers the Yup message that says the field is required even if there is data in it - why is this happening? The field is not empty, that's the situation when it must show that message.

And of course, it doesn't update the entity when I click to save it because it requires those fields.

Here is the code:

import React from 'react';
...

class CreateCompanyForm extends React.PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      name: '',
      contactMail: '',
      redirectCreate: false,
      redirectEdit: false,
      edit: false,
    };
  }

  ponentDidMount() {
    const {
      getCompany,
      location: { pathname },
    } = this.props;
    if (pathname.substring(11) !== 'create') { // checks the URL if it is in edit mode 
      getCompany(pathname.substring(16));
      this.setState({
        edit: true,
      });
      this.setState({
        name: this.propspany.name,
        contactMail: this.propspany.contactMail,
      });
    }
  }

    onChange = (e, { name, value }) => { // method to update the state with modified value in input
      this.setState({ [name]: value });
    };

  handleSubmit = values => {
    const { createCompany, getCompanies } = this.props;
    createCompany(values);
    this.setState({ redirectCreate: true });
    getCompanies(this.props.query);
  };

    handleSubmitEdit = e => {
      e.preventDefault();
      const { name, contactMail } = this.state;
      const { updateCompany } = this.props;
      updateCompany(this.propspany._id, {
        name,
        contactMail,
      });
      this.setState({ redirectEdit: true });
    };

  render() {
    let title = 'Create Company';
    let buttonName = 'Create';
    let submit = this.handleSubmitCreate;

    const { redirectCreate, redirectEdit } = this.state;

    if (redirectCreate) {
      return <Redirect to="/panies" />;
    }

    if (redirectEdit) {
      return <Redirect to={`/panies/${this.propspany._id}`} />;
    }

    if (this.state.edit) {
      title = 'Edit Company';
      buttonName = 'Edit';
      submit = this.handleSubmitEdit;
    }

    const initialValues = {
      name: '',
      contactMail: '',
    };
    const requiredErrorMessage = 'This field is required';
    const emailErrorMessage = 'Please enter a valid email address';
    const validationSchema = Yup.object({
      name: Yup.string().required(requiredErrorMessage),
      contactMail: Yup.string()
        .email(emailErrorMessage)
        .required(requiredErrorMessage),
    });
    return (
      <Layout>
        <div>
          <Button type="submit" form="amazing">
            Create pany
          </Button>
          <Button onClick={() => this.props.history.goBack()}>Discard</Button>
          <div>Create pany</div>
        </div>

        <Formik
          htmlFor="amazing"
          initialValues={initialValues}
          validationSchema={validationSchema}
          onSubmit={values => this.handleSubmit(values)}>
          {({ values, errors, touched, setValues }) => (
            <Form id="amazing">
              <Grid columns={2}>
                <Grid.Column>
                  <Label>Company Name</Label>
                  <Field
                    name="name"
                    as={Input}
                    placeholder="Hello"
                    value={this.state.name || ''} // takes the value from the state
                    onChange={this.onChange} // does the changing 
                  />
                  <div>{touched.name && errors.name ? errors.name : null}</div>
                </Grid.Column>

                <Grid.Column>
                  <Label>Contact Mail</Label>
                  <Field
                    name="contactMail"
                    as={Input}
                    placeholder="[email protected]"
                    value={this.state.contactMail || ''} // takes the value from the state
                    onChange={this.contactMail} // does the changing 
                  />
                  <div>
                    {touched.contactMail && errors.contactMail
                      ? errors.contactMail
                      : null}
                  </div>
                </Grid.Column>
              </Grid>
            </Form>
          )}
        </Formik>
      </Layout>
    );
  }
}

...

export default connect(mapStateToProps, mapDispatchToProps)(CreateCompanyForm);

Any ideas about how to solve this and make the fields editable and remove the 'This field is required' message when the field already has data?

I'm using Formik and Yup when I create an entity (pany in this case) in order to check if the fields are correct and all of them are introduced (all are required).

When I create an entity it works fine: it only lets you create it if all fields are introduced and the rules are fulfilled (only one rule at the moment for the email).

This is the code which works to create a new pany with 2 fields name and email:

    import React from 'react';
    import { Redirect } from 'react-router-dom';
    import { Formik, Form, Field } from 'formik';
    import { Input, Button, Label, Grid } from 'semantic-ui-react';
    import { connect } from 'react-redux';
    import * as Yup from 'yup';
    import { Creators } from '../../../actions';
    import Layout from '../../Layout/Layout';
    
    class CreateCompanyForm extends React.PureComponent {
      constructor(props) {
        super(props);
    
        this.state = {
          name: '',
          contactMail: '',
          redirectCreate: false,
          redirectEdit: false,
          edit: false,
        };
      }
    
      ponentDidMount() {
        const {
          getCompany,
          location: { pathname },
        } = this.props;
      }
    
      handleSubmit = values => {
        const { createCompany, getCompanies } = this.props;
        createCompany(values);
        this.setState({ redirectCreate: true });
        getCompanies(this.props.query);
      };
    
      render() {
        let title = 'Create Company';
        let buttonName = 'Create';
        let submit = this.handleSubmitCreate;
    
        const { redirectCreate, redirectEdit } = this.state;
    
        if (redirectCreate) {
          return <Redirect to="/panies" />;
        }
        const initialValues = {
          name: '',
          contactMail: '',
        };
        const requiredErrorMessage = 'This field is required';
        const emailErrorMessage = 'Please enter a valid email address';
        const validationSchema = Yup.object({
          name: Yup.string().required(requiredErrorMessage),
          contactMail: Yup.string()
            .email(emailErrorMessage)
            .required(requiredErrorMessage),
        });
        return (
          <Layout>
            <div>
              <Button type="submit" form="amazing">
                Create pany
              </Button>
              <Button onClick={() => this.props.history.goBack()}>Discard</Button>
              <div>Create pany</div>
            </div>
    
            <Formik
              htmlFor="amazing"
              initialValues={initialValues}
              validationSchema={validationSchema}
              onSubmit={values => this.handleSubmit(values)}>
              {({ values, errors, touched, setValues }) => (
                <Form id="amazing">
                  <Grid columns={2}>
                    <Grid.Column>
                      <Label>Company Name</Label>
                      <Field name="name" as={Input} placeholder="Hello" />
                      <div>{touched.name && errors.name ? errors.name : null}</div>
                    </Grid.Column>
    
                    <Grid.Column>
                      <Label>Contact Mail</Label>
                      <Field
                        name="contactMail"
                        as={Input}
                        placeholder="[email protected]"
                      />
                      <div>
                        {touched.contactMail && errors.contactMail
                          ? errors.contactMail
                          : null}
                      </div>
                    </Grid.Column>
                  </Grid>
                </Form>
              )}
            </Formik>
          </Layout>
        );
      }
    }
    
    const mapStateToProps = state => ({
      panies: state.panies.panies,
      pany: state.panies.selectedCompany,
      query: state.panies.query,
    });
    
    const mapDispatchToProps = {
      getCompanies: Creators.getCompaniesRequest,
      createCompany: Creators.createCompanyRequest,
      getCompany: Creators.getCompanyRequest,
      updateCompany: Creators.updateCompanyRequest,
    };

export default connect(mapStateToProps, mapDispatchToProps)(CreateCompanyForm);

The problem appears when I want to edit the pany. So when someone click on a pany on edit button it should open the pany with all its fields containing the current values which should be editable.

To get those current values I'm using the state, for example the email can be accessed from this.state.email and in order to change the value it was added onChange method.

The values can be modified in the text input. However, it triggers the Yup message that says the field is required even if there is data in it - why is this happening? The field is not empty, that's the situation when it must show that message.

And of course, it doesn't update the entity when I click to save it because it requires those fields.

Here is the code:

import React from 'react';
...

class CreateCompanyForm extends React.PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      name: '',
      contactMail: '',
      redirectCreate: false,
      redirectEdit: false,
      edit: false,
    };
  }

  ponentDidMount() {
    const {
      getCompany,
      location: { pathname },
    } = this.props;
    if (pathname.substring(11) !== 'create') { // checks the URL if it is in edit mode 
      getCompany(pathname.substring(16));
      this.setState({
        edit: true,
      });
      this.setState({
        name: this.props.pany.name,
        contactMail: this.props.pany.contactMail,
      });
    }
  }

    onChange = (e, { name, value }) => { // method to update the state with modified value in input
      this.setState({ [name]: value });
    };

  handleSubmit = values => {
    const { createCompany, getCompanies } = this.props;
    createCompany(values);
    this.setState({ redirectCreate: true });
    getCompanies(this.props.query);
  };

    handleSubmitEdit = e => {
      e.preventDefault();
      const { name, contactMail } = this.state;
      const { updateCompany } = this.props;
      updateCompany(this.props.pany._id, {
        name,
        contactMail,
      });
      this.setState({ redirectEdit: true });
    };

  render() {
    let title = 'Create Company';
    let buttonName = 'Create';
    let submit = this.handleSubmitCreate;

    const { redirectCreate, redirectEdit } = this.state;

    if (redirectCreate) {
      return <Redirect to="/panies" />;
    }

    if (redirectEdit) {
      return <Redirect to={`/panies/${this.props.pany._id}`} />;
    }

    if (this.state.edit) {
      title = 'Edit Company';
      buttonName = 'Edit';
      submit = this.handleSubmitEdit;
    }

    const initialValues = {
      name: '',
      contactMail: '',
    };
    const requiredErrorMessage = 'This field is required';
    const emailErrorMessage = 'Please enter a valid email address';
    const validationSchema = Yup.object({
      name: Yup.string().required(requiredErrorMessage),
      contactMail: Yup.string()
        .email(emailErrorMessage)
        .required(requiredErrorMessage),
    });
    return (
      <Layout>
        <div>
          <Button type="submit" form="amazing">
            Create pany
          </Button>
          <Button onClick={() => this.props.history.goBack()}>Discard</Button>
          <div>Create pany</div>
        </div>

        <Formik
          htmlFor="amazing"
          initialValues={initialValues}
          validationSchema={validationSchema}
          onSubmit={values => this.handleSubmit(values)}>
          {({ values, errors, touched, setValues }) => (
            <Form id="amazing">
              <Grid columns={2}>
                <Grid.Column>
                  <Label>Company Name</Label>
                  <Field
                    name="name"
                    as={Input}
                    placeholder="Hello"
                    value={this.state.name || ''} // takes the value from the state
                    onChange={this.onChange} // does the changing 
                  />
                  <div>{touched.name && errors.name ? errors.name : null}</div>
                </Grid.Column>

                <Grid.Column>
                  <Label>Contact Mail</Label>
                  <Field
                    name="contactMail"
                    as={Input}
                    placeholder="[email protected]"
                    value={this.state.contactMail || ''} // takes the value from the state
                    onChange={this.contactMail} // does the changing 
                  />
                  <div>
                    {touched.contactMail && errors.contactMail
                      ? errors.contactMail
                      : null}
                  </div>
                </Grid.Column>
              </Grid>
            </Form>
          )}
        </Formik>
      </Layout>
    );
  }
}

...

export default connect(mapStateToProps, mapDispatchToProps)(CreateCompanyForm);

Any ideas about how to solve this and make the fields editable and remove the 'This field is required' message when the field already has data?

Share Improve this question edited Sep 7, 2020 at 21:03 Luis Paulo Pinto 6,0464 gold badges22 silver badges36 bronze badges asked Aug 21, 2020 at 15:06 Leo MessiLeo Messi 6,23622 gold badges80 silver badges155 bronze badges 3
  • why are you not setting email in initialValues ? so if you are editing: pass data with props and use them as initialValues – Tobias Lins Commented Sep 7, 2020 at 8:38
  • @TobiasLins it is set there: const initialValues = { name: '', contactMail: '', }; – Leo Messi Commented Sep 7, 2020 at 8:42
  • yeah but you are setting it to empty string and not to the value from editing – Tobias Lins Commented Sep 7, 2020 at 9:02
Add a ment  | 

1 Answer 1

Reset to default 6 +100

You need to do 3 little changes:

1. Your inital value is always set as:

const initialValues = {
   name: '',
   contactMail: '',
};

You need to change it to:

const initialValues = {
   name: this.state.name,
   contactMail: this.state.contactMail,
};

2. Add enableReinitialize to Formik

Even with the change nº 1, your submit will still throwing errors, that´s because when the ponent is created, Formik is rendered with the values from your constructor:

this.state = {
      name: "",
      contactMail: "",
      redirectCreate: false,
      redirectEdit: false,
      edit: false,
    };

And when you change state inside ponentDidMount, Formik is not reinitialize with the update values:

ponentDidMount() {
    const {
      getCompany,
      location: { pathname },
    } = this.props;
    if (pathname.substring(11) !== 'create') { // checks the URL if it is in edit mode 
      getCompany(pathname.substring(16));
      this.setState({
        edit: true,
      });
      this.setState({
        name: this.props.pany.name,
        contactMail: this.props.pany.contactMail,
      });
    }
  }

So, to formik reinitialize you need to add enableReinitialize to it, like this:

  <Formik
     htmlFor="amazing"
     /// HERE'S THE CODE
     enableReinitialize
     initialValues={initialValues}
     ....

3. With enableReinitialize, Formik will trigger validation on Blur and on Change. To avoid this you can add validateOnChange and validateOnBlur to false:

 <Formik
   htmlFor="amazing"
   enableReinitialize
   validateOnChange={false}
   validateOnBlur={false}
   initialValues={initialValues}
   .....
发布评论

评论列表(0)

  1. 暂无评论