【问题标题】:Redux forms: how to handle errors in form componentRedux 表单:如何处理表单组件中的错误
【发布时间】:2018-07-18 08:45:23
【问题描述】:

现在我有这样的错误:

Unhandled Rejection (SubmissionError): Submit Validation Failed

  27 |       .catch(err => {
> 28 |         return ErrorHandler.raiseAnError(err);
  29 |       });

这是我的代码:

TractorForm.js

import React, { Component } from "react";
import PropTypes from "prop-types";
import { reduxForm } from "redux-form";
import FormInput from "../Shared/FormInput";
import GlobalConst from "../GlobalConst";
import { Link } from "react-router-dom";
import SelectInput from "../Shared/SelectInput";
import ErrorHandler from "../ErrorHandler";

const manufacturers = ["DAF", "VOLVO", "SCANIA", "MAN", "IVECO"];

class TractorFormWrapper extends Component {
  state = {
    loading: false,
    isSubmitted: false
  };

  onFormSubmit = e => {
    e.preventDefault();

    this.setState({ isSubmitted: true });
    if (this.props.valid) {
      this.setState({ loading: true });
      this.props
        .handleSubmit(e)
        .then(() => alert("is ok!"))
        .catch(err => {
          return ErrorHandler.raiseAnError(err);
        });
    }
  };

  render() {
    const { submitText, error } = this.props;
    const { loading, isSubmitted } = this.state;
    const formClassNames = loading ? "ui form loading" : "ui form";
    return (
      <form className={formClassNames} onSubmit={this.onFormSubmit}>
        <div className="ui grid fields">
          <div className="sixteen wide eight wide computer column">
            <SelectInput
              name="manufacturer"
              type="text"
              label="Производитель"
              validations={[GlobalConst.REQUIRED]}
              isSubmitted={isSubmitted}
              values={manufacturers}
            />
          </div>

          <div className="sixteen wide eight wide computer column">
            <FormInput
              name="model"
              type="text"
              label="Модель"
              validations={[GlobalConst.REQUIRED]}
              isSubmitted={isSubmitted}
            />
          </div>

          <div className="sixteen wide column">
            <FormInput
              name="description"
              type="textarea"
              label="Описание"
              isSubmitted={isSubmitted}
            />
          </div>
        </div>

        {error && (
          <div className="ui red message">
            <strong>{error}</strong>
          </div>
        )}

        <div className="ui fluid buttons">
          <button
            className="ui primary button"
            type="submit"
            disabled={loading}
          >
            {submitText}
          </button>

          <Link to="/tractors" className="ui button">
            Отмена
          </Link>
        </div>
      </form>
    );
  }
}

let TractorForm = {};

TractorForm.propTypes = {
  submitText: PropTypes.string
};

TractorForm.defaultProps = {
  submitText: "Отправить"
};

TractorForm = reduxForm({
  form: "tractor"
})(TractorFormWrapper);

export default TractorForm;

TractorAdd.js

import React, { Component } from "react";
import TractorForm from "./TractorForm";
import TractorApi from "./TractorApi";
import { toast } from "react-semantic-toasts";
import ErrorHandler from "../ErrorHandler";

class TractorAdd extends Component {
  state = {};

  submit = values =>
    TractorApi.create(values).then(
      () => {
        toast({
          type: "success",
          icon: "truck",
          title: "Тягач создан",
          description: ""
        });
        this.props.history.push("/tractors");
      },
      error => {
        return Promise.reject(error);
      }
    );

  render() {
    return (
      <div>
        <TractorForm onSubmit={this.submit} submitText="Создать" />
      </div>
    );
  }
}

export default TractorAdd;

ErrorHandler.js

import { SubmissionError } from "redux-form";

export default {
  raiseAnError: error => {
    if (
      error.response.data.hasOwnProperty("message") &&
      error.response.data.hasOwnProperty("stackHighlighted")
    ) {
      throw new SubmissionError({
        _error: error.response.data.hasOwnProperty("message") || "error"
      });
    } else {
      const errKeys = Object.keys(error.response.data);
      const errObj = {};
      for (const errItem of errKeys) {
        errObj[errItem] = error.response.data[errItem]["message"];
      }

      errObj["_error"] = "Произошла ошибка!";

      throw new SubmissionError(errObj);
    }
  }
};

为什么我需要这个形式而不是添加组件?所以我可以用loading状态变量修复逻辑并重新提交表单

我的_error 也由于某些原因无法正常工作,但我已经按照文档中的方式完成了所有操作:https://redux-form.com/7.3.0/examples/submitvalidation/

我做错了什么以及如何处理表单组件中的 SubmissionError?

【问题讨论】:

  • 例如:如果我写error =&gt; { throw new Error({}); } - 一切都很好,并且表单组件 then().catch() 去捕获块,但是当我使用 SubmissionError - 它去 then (如承诺没问题,但不是!)

标签: javascript reactjs promise redux-form


【解决方案1】:

您的错误处理不起作用的原因是您应该在handleSubmit 函数本身中抛出新的SubmissionError like the example you linked

<form onSubmit={handleSubmit(submit)}>

function submit(values) {
  return sleep(1000).then(() => {
    // If error
    throw new SubmissionError({
      password: 'Wrong password',
      _error: 'Login failed!'
    })

    // The rest logic here ...
  })
}

所以你应该稍微重构你的代码,像这样(按照 cmets):

<form className={formClassNames} onSubmit={handleSubmit(values => {
 // 1. Your submit logic should be here.
 // 2. Better to organize it in a stand-alone function, as `submit` function from the above example.
 // 3. If you throw SubmissionError here, the error handling will work.
 throw new SubmissionError({
   _error: 'Error'
 })
)}>

尝试调整和简化您的代码,就像您提供的官方库示例一样。


更新 1 - 几乎是一个完整的示例。请严格遵守cmets:

* 我删除了一些与问题无关的代码块

TractorForm - 它将被重复用于 EditAdd(创建)操作。

class TractorForm extends Component {

  render() {
    const { handleSubmit, error } = this.props;
    return (
      <form onSubmit={handleSubmit}>

        // Rest input fields should be here ...

        { error && (
          <div className="ui red message">
            <strong>{error}</strong>
          </div>
        )}

        <div className="ui fluid buttons">
          <button
            className="ui primary button"
            type="submit"
            disabled={loading}
          >
            {submitText}
          </button>
        </div>
      </form>
    );
  }
}

export default reduxForm({
  form: "tractor"
})(TractorForm);

TractorAdd - 用于添加新的拖拉机。同样的逻辑,你可以申请Edit Tractor。你必须创建一个新的TractorEdit 组件,它将onSubmit 函数传递给TractorForm

class TractorAdd extends Component {

  onSubmit (values) {
    // Please make sure here you return the promise result. It's a `redux-form` gotcha.
    // If you don't return it, error handling won't work.
    return TractorApi.create(values).then(
      () => {
        toast({
          type: "success",
          icon: "truck",
          title: "Тягач создан",
          description: ""
        });
        this.props.history.push("/tractors");
      },
      error => {
        // 1. Here you should throw new SubmissionError.
        // 2. You should normalize the error, using some parts of `ErrorHandler` function
        throw new SubmissionError(error)
      }
    );
  }

  render() {
    return <div>
      <TractorForm onSubmit={this.onSubmit} submitText="Создать" />
    </div>
  }
}

export default TractorAdd;

更新 2 - 让您的实现保持原样,但稍微更改您的 TractorFormWrapper onFormSubmit 及其用法:

文档说明here

TractorForm

class TractorFormWrapper extends Component {
  state = {
    loading: false,
    isSubmitted: false
  };

  onFormSubmit = data => {

    this.setState({ isSubmitted: true });
    if (this.props.valid) {
      this.setState({ loading: true });

      // `onSubmit` comes from `TractorAdd`
      return this.props.onSubmit(data)
        .then(() => alert("is ok!"))
        .catch(err => {
          return ErrorHandler.raiseAnError(err);
        });
    }
  };

  render() {
    const { handleSubmit } = this.props

    return <form onSubmit={handleSubmit(this.onFormSubmit)}>The rest logic is here ...</form>
  }
}

let TractorForm = {};

TractorForm.propTypes = {
  submitText: PropTypes.string
};

TractorForm.defaultProps = {
  submitText: "Отправить"
};

TractorForm = reduxForm({
  form: "tractor"
})(TractorFormWrapper);

export default TractorForm;

【讨论】:

  • 好的,但是如何this.props .handleSubmit(e) .then(() =&gt; alert("is ok!")) .catch(err =&gt; { return ErrorHandler.raiseAnError(err); }); 进入这里?
  • 这也是一个非常糟糕的主意:在表单组件中编辑和添加相同的代码:这就是我使用 formAdd 组件和“嵌套”form 组件的原因:表单是隔离,服务器调用在另一个逻辑组件中
  • 我添加了几乎完整的示例。请按照我的回答反馈的想法和我为您提供的示例进行操作。
  • 谢谢,但您仍然缺少一个非常重要的部分:handleSubmit 没有在表单组件中重新定义 => 我无法添加 ui spinner-loader && 禁用按钮。
  • onFormSubmit=e=&gt; { e.preventDefault(); this.setState({ isSubmitted: true }); // so I can display error messages only when I clicked on submit button if (this.props.valid) { // form is valid? let's send it to the server, not when it's invalid this.setState({ loading: true }); // let's display spinner this.props .handleSubmit(e) .then(() =&gt; alert("is ok!")) // here we have to hide spinner and do nothing else... .catch(err =&gt; { return ErrorHandler.raiseAnError(err); // here we have to hide spinner and do something else if needed in the future, but it's always going into .then block :() }); } };
猜你喜欢
  • 2018-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-23
  • 1970-01-01
  • 1970-01-01
  • 2014-11-25
  • 1970-01-01
相关资源
最近更新 更多