How to properly use Formik's setError method? (React library)

后端 未结 4 1087
难免孤独
难免孤独 2021-02-04 00:26

I am using React communicating with a backend. Now trying to properly implement Formik (Form library).

Main question: How do I properly use Formik\'s setError me

相关标签:
4条回答
  • 2021-02-04 00:34
    <Formik
      validationSchema={schema}
      initialValues={{ email: '', pswrd: '' }}
      onSubmit={(values, actions) => {
        // initialise error status <---- 1
        actions.setStatus(undefined); 
    
        setTimeout(() => {
    
          // setting error status <---- 2
          actions.setStatus({
            email: 'This is email already exists.',
            pswrd: 'This is password is incorrect',
          });
    
        }, 500);
      }}
      // destructuring status <---- 3
      render={({ handleSubmit, handleChange, handleBlur, values, errors, status }) => (
        <form onSubmit={handleSubmit}>
          <input
            type="text"
            name="email"
            value={values['email']}
            onChange={handleChange}
            onBlur={handleBlur}
          />
          <input
            type="text"
            name="pswrd"
            value={values['pswrd']}
            onChange={handleChange}
            onBlur={handleBlur}
          />
          <button type="submit">Submit</button>
    
          // using error status <---- 4
          {status && status.email ? (
            <div>API Error: {status.email}</div>
          ) : (
            errors.email && <div>Validation Error: {errors.email}</div>
          )}
    
          {status && status.pswrd ? (
            <div>API Error: {status.pswrd}</div>
          ) : (
            errors.pswrd && <div>Validation Error: {errors.pswrd}</div>
          )}
        </form>
      )}
    />
    
    0 讨论(0)
  • 2021-02-04 00:38

    Formik author here...

    setError was deprecated in v0.8.0 and renamed to setStatus. You can use setErrors(errors) or setStatus(whateverYouWant) in your handleSubmit function to get the behavior you want here like so:

    handleSubmit = async (values, { setErrors, resetForm }) => {
       try {
         // attempt API call
       } catch(e) {
         setErrors(transformMyApiErrors(e))
         // or setStatus(transformMyApiErrors(e))
       }
    }
    
    

    What's the difference use setStatus vs. setErrors?

    If you use setErrors, your errors will be wiped out by Formik's next validate or validationSchema call which can be triggered by the user typing (a change event) or blurring an input (a blur event). Note: this assumed you have not manually set validateOnChange and validateOnBlur props to false (they are true by default).

    IMHO setStatus is actually ideal here because it will place the error message(s) on a separate part of Formik state. You can then decide how / when you show this message to the end user like so.

    // status can be whatever you want
    {!!status && <FormError>{status}</FormError>}
    // or mix it up, maybe transform status to mimic errors shape and then ...
    {touched.email && (!!errors.email && <FormError>{errors.email}</FormError>) || (!!status && <FormError>{status.email}</FormError>) }
    

    Be aware that the presence or value of status has no impact in preventing the next form submission. Formik only aborts the submission process if validation fails.

    0 讨论(0)
  • 2021-02-04 00:38

    I just solved my own problem.

    I needed to use:

    setErrors( errors )
    

    instead of:

    setErrors({ errors })
    
    0 讨论(0)
  • 2021-02-04 00:45

    Another way to handle this situation is to assign a specific key to your api errors and use setStatus for status messages.

    __handleSubmit = (values, {setStatus, setErrors}) => {
      return this.props.onSubmit(values)
        .then(() => {
          setStatus("User was updated successfully.");
        })
        .catch((err) => {
          setErrors({api: _.get(err, ["message"])});
        });
    }
    

    Then any validation errors would appear by the fields and any api errors could appear at the bottom:

    <Formik
      validationSchema={LoginSchema}
      initialValues={{login: ""}}
      onSubmit={this.__handleSubmit}
    >
      {({isSubmitting, status, errors, values, setFieldValue}) => (
        <Form className={classNames("form")}>
          <FormGroup>
            <InputGroup>
              <InputGroup.Text>
                <FontAwesomeIcon icon={faUser} fixedWidth />
              </InputGroup.Text>
              <Field
                name="login"
                type={"text"}
                placeholder="Login"
                className="form-control"
              />
            </InputGroup>
            <ErrorMessage name="login" />
          </FormGroup>
          <Button type="submit" variant="primary" disabled={isSubmitting}>
            Submit
          </Button>
          {errors && _.has(errors, ["api"]) && <div className="text-danger">{_.get(errors, ["api"])}</div>}
          {status && <div className="text-success">{status}</div>}
        </Form>
      )}
    </Formik>
    

    Don't forget about the schema...

    const LoginSchema = Yup.object().shape({
      login: Yup.string()
        .min(4, 'Too Short!')
        .max(70, 'Too Long!')
        .required('Login is required'),
    });
    

    The api error message will show until the next validation call by Formik (i.e. the user is fixing something). But the status message will stay until you clear it (with a timer or Fade).

    0 讨论(0)
提交回复
热议问题