javascript 如何正确使用 Formik 的 setError 方法?(反应库)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/52986962/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 10:04:42  来源:igfitidea点击:

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

javascriptreactjsformik

提问by Rik Schoonbeek

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

我正在使用 React 与后端通信。现在尝试正确实现 Formik(表单库)。

Main question: How do I properly use Formik's setError method?

主要问题: 如何正确使用 Formik 的 setError 方法?

Client side validation errors show properly, but now I am trying to set/show the backend validation errors, which are returned with a response with status code 400.

客户端验证错误显示正确,但现在我正在尝试设置/显示后端验证错误,这些错误与状态代码 400 的响应一起返回。

Linkto the docs on the method I am trying to use

链接到我尝试使用的方法的文档

I am using this method in the method named handle400Errorin the code below.

我在下面代码中名为handle400Error 的方法中使用此方法。

My React (and Formik) code:

我的 React(和 Formik)代码:

import React, { Component } from "react";
import axios from "axios";
import { Formik } from "formik";
import * as Yup from "yup";
import styled from "styled-components";
import FormError from "../formError";

const Label = styled.label``;

class LoginForm extends Component {
  initialValues = {
    password: "",
    username: ""
  };

  getErrorsFromValidationError = validationError => {
    const FIRST_ERROR = 0;
    return validationError.inner.reduce((errors, error) => {
      return {
        ...errors,
        [error.path]: error.errors[FIRST_ERROR]
      };
    }, {});
  };

  getValidationSchema = values => {
    return Yup.object().shape({
      password: Yup.string()
        .min(6, "Password must be at least 6 characters long")
        .required("Password is required!"),
      username: Yup.string()
        .min(5, "Username must be at least 5 characters long")
        .max(40, "Username can not be longer than 40 characters")
        .required("Username is required")
    });
  };

  handleSubmit = async (values, { setErrors }) => {
    console.log("handleSubmit");

    try {
      const response = await axios.post(
        "http://127.0.0.1:8000/rest-auth/login/",
        values
      );
      const loginToken = response.data["key"];
      this.handleLoginSuccess(loginToken);
    } catch (exception) {
      // Expected: 400 status code
      if (exception.response && exception.response.status === 400) {
        // Display server validation errors
        this.handle400Error(exception.response.data, setErrors);
      }
      console.log("exception", exception);
      console.log("exception.response", exception.response);
    }
  };

  handle400Error = (backendErrors, setErrors) => {
    let errors = {};
    for (let key in backendErrors) {
      errors[key] = backendErrors[key][0]; // for now only take the first error of the array
    }
    console.log("errors object", errors);
    setErrors({ errors });
  };

  handleUnexpectedError = () => {};

  handleLoginSuccess = loginToken => {
    console.log("handleLoginSuccess");
    this.props.setGreeneryAppState({
      loginToken: loginToken
    });
    this.props.history.replace(`/${this.props.locale}/`);
  };

  validate = values => {
    const validationSchema = this.getValidationSchema(values);
    try {
      validationSchema.validateSync(values, { abortEarly: false });
      return {};
    } catch (error) {
      return this.getErrorsFromValidationError(error);
    }
  };

  render() {
    return (
      <React.Fragment>
        <h1>Login</h1>
        <Formik
          initialValues={this.initialValues}
          validate={this.validate}
          validationSchema={this.validationSchema}
          onSubmit={this.handleSubmit}
          render={({
            errors,
            touched,
            values,
            handleBlur,
            handleChange,
            handleSubmit
          }) => (
            <form onSubmit={handleSubmit}>
              {errors.non_field_errors && (
                <formError>{errors.non_field_errors}</formError>
              )}
              <Label>Username</Label>
              <input
                onChange={handleChange}
                onBlur={handleBlur}
                value={values.username}
                type="text"
                name="username"
                placeholder="Enter username"
              />
              {touched.username &&
                errors.username && <FormError>{errors.username}</FormError>}
              <Label>Password</Label>
              <input
                onChange={handleChange}
                onBlur={handleBlur}
                value={values.password}
                type="password"
                name="password"
                placeholder="Enter password"
              />
              {touched.password &&
                errors.password && <FormError>{errors.password}</FormError>}
              <button type="submit">Log in</button>
            </form>
          )}
        />
      </React.Fragment>
    );
  }

回答by jaredpalmer

Formik author here...

Formik 作者在这里...

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

setError在 v0.8.0 中弃用并重命名为setStatus. 您可以在您的函数中使用setErrors(errors)或来获得您想要的行为,如下所示:setStatus(whateverYouWant)handleSubmit

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

What's the difference use setStatusvs. setErrors?

use setStatusvs. 有setErrors什么区别?

If you use setErrors, your errors will be wiped out by Formik's next validateor validationSchemacall 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 validateOnChangeand validateOnBlurprops to false(they are trueby default).

如果您使用setErrors,您的错误将被 Formik 的 nextvalidatevalidationSchemacall消除,这可以由用户键入(更改事件)或模糊输入(模糊事件)触发。注意:这假设您没有手动设置validateOnChangevalidateOnBlur道具falsetrue默认情况下)。

IMHO setStatusis 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.

恕我直言,setStatus这里实际上是理想的,因为它将错误消息放在 Formik 状态的单独部分。然后,您可以决定如何/何时向最终用户显示此消息。

// 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 statushas no impact in preventing the next form submission. Formik only aborts the submission process if validation fails.

请注意, 的存在或值对status阻止下一次表单提交没有影响。Formik 仅在验证失败时中止提交过程

回答by Rik Schoonbeek

I just solved my own problem.

我刚刚解决了我自己的问题。

I needed to use:

我需要使用:

setErrors( errors )

instead of:

代替:

setErrors({ errors })

回答by redheadedstep

Another way to handle this situation is to assign a specific key to your apierrors and use setStatusfor status messages.

处理这种情况的另一种方法是为您的api错误分配一个特定的键并setStatus用于状态消息。

__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:

然后任何验证错误都会出现在字段中,任何 api 错误都可能出现在底部:

<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 apierror message will show until the next validation call by Formik (i.e. the user is fixing something). But the statusmessage will stay until you clear it (with a timer or Fade).

api错误信息将显示,直到通过Formik下次验证调用(即,用户在固定的东西)。但status消息会一直保留,直到您清除它(使用计时器或淡入淡出)。