jaredpalmer / formik

Build forms in React, without the tears 😭
https://formik.org
Apache License 2.0
34.02k stars 2.79k forks source link

Formik + Yup | How to render split views for Edit/Create Form #1401

Closed EfstathiadisDimitris closed 5 years ago

EfstathiadisDimitris commented 5 years ago

My question revolves around a particular scenario I am currently facing. I am using the withFormik H.O.C, along with Yup, to handle various cases in my forms, such as submitting, errorHandling, and a few more depending on the situation.

Usually, my form situation involves a Create and an Edit Mode. onCreate => Pass the defaultvalues and call the POST Method, from my services. onEdit => Populate the values with the current Item Values from the Server, and call the PUT Method from the services.

Example


  const validationSchema = Yup.object().shape({
    username: Yup.string('Provide a Username').required('Username is Required'),
    email: Yup.string().email('Provide a Valid email Address'),
    password: Yup.string('Provide a Password').required('Password is required'),
    confirmPassword: Yup.string('Provide your password again')
      .required('Password Confirmation is Required')
      .oneOf([Yup.ref('password')], 'Passwords do not match'),
  });

  const EnchancedCreateUserForm = withFormik({
    mapPropsToValues: ({
      user = {
        username: '',
        email: '',
        password: '',
        confirmPassword: '',
      }
    }) => ({ ...user }),

    validationSchema,

    handleSubmit: (values, { props, setSubmitting }) => {
      const { doSubmit, onSave, inEditMode } = props;

      const saveUser = inEditMode ? updateUser : createUser;
      return doSubmit(saveUser, values)
        .then(() => {
          setSubmitting(false);
          onSave();
        })
        .catch(() => {
          setSubmitting(false);
        });
    },

    displayName: 'AddEditUser'
  })(AddEditUser);

That is actually been working great for me, since my Create and Edit Form are the same. And here lay my 2 problems.

Current Situation

My current form implementation has 2 views. One unified one on Create with those 4 fields, and on Edit, I have 2 forms. One for passwordChange and one for infoChange. Which makes me face the following problems.

  1. I would need 3 FormValidationSchemas(CREATE, EDIT-INFO, EDIT-PASSWORD). Which I am not sure formik even supports.
  2. How exactly, should I handle the rest of the functionality, onSubmit, ErrorMessage for both field error and statusError?

If you could actually help me out figure a way of attack it would be great.

I read the validationSchema can be passed a function that returns a validationSchema, so I did this, but it is not working:

  const validationFullSchema = Yup.object().shape({
    username: Yup.string('Provide a Username').required('Username is Required'),
    email: Yup.string().email('Provide a Valid email Address'),
    password: Yup.string('Provide a Password').required('Password is required'),
    confirmPassword: Yup.string('Provide your password again')
      .required('Password Confirmation is Required')
      .oneOf([Yup.ref('password')], 'Passwords do not match'),
  });

  const validationEditSchema = Yup.object().shape({
    username: Yup.string('Provide a Username').required('Username is Required'),
    email: Yup.string().email('Provide a Valid email Address'),
  });

  validationSchema: validationSchemaFn(),
  // Which throws an error
  // Failed to load app. Error: Cannot read property 'props' of undefined

I probably am doing something wrong, but I put it here just in case.

My Opinion on the matter

What do you of my proposed solution? If you like it, could you please make it better or help with some code samples, espacially in the multiple validationSchemas issue. Thank you!!

jaredpalmer commented 5 years ago

Hey! Thanks for the write up. How best to structure Create vs. Edit is a decision that comes up in pretty much every single application. Unfortunately, the answer is: it depends. The good news is that your reasoning about this problem is 100% on track. In general, I like to to keep things that are similar (i.e. likely to be changed at the same time) as close as possible and separate things that are different (i.e. unlikely to be changed at the same time). Some observations/guesses...

Cognizant of the above, I suggest actually making 3 forms. While yes, you can use the function version of validation schema, I find that this tends to get messy when you have more than 2 versions of your form. Since you are breaking out edit user info and edit password, I would thus split each of these up into totally separate <Formik>'s with their own validationSchema's (although you could in theory share the parts of the inner object). FWIW in our apps, this decision is very clear cut because we make a special update password endpoint (that's different from update user) that takes the old password and the new one (and confirms that they are indeed different values).

I hope that helps.

Side note: Very very clever to use Yup.oneOf() for matching. In the past, we've just written out our own Yup.equalTo as described in https://github.com/jquense/yup/issues/97