Redux form validation runs after onChange handler

2019-08-27 08:19发布

问题:

I have the following redux form:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm, getFormSyncErrors } from 'redux-form';

import { createExpense } from '../../actions';
import { validateName, validateImage } from '../../helpers/expense_utils';
import { renderInputField, validate, renderTextAreaField, renderDropzoneField } from '../../helpers/form_utils';

const validators = [
  {
    field: 'title',
    validator: validateName
  },
  {
    field: 'description',
    validator: validateDescription
  },
  {
    field: 'amount',
    validator: validateAmount
  },
  {
    field: 'image',
    validator: validateImage
  }
];

class NewExpense extends Component {

  constructor(props) {
    super(props);
    this.state = {
      error: undefined,
      imagePreviewUrl: undefined
    };
  }

  _onSubmit = values => {
    let formData = new FormData();
    formData.append('title', values.title);
    formData.append('description', values.description);
    formData.append('amount', values.amount);
    formData.append('file', values.image[0]);
    this.props.createExpense(formData, this.props.groupId, () => this.props.onClose(), error => this.setState({error: error}));
  }

  _onImagePreviewChange = files => {
    //this.props.inputErrors.image is one step behind 
    this.setState({
      imagePreviewUrl: files[0].preview
    });
  }

  render() {
    const { handleSubmit } = this.props;
    const { imagePreviewUrl } = this.state;
    return (
      <div>
        <form onSubmit={ handleSubmit(this._onSubmit.bind(this)) }>
          <Field name="title" label="Name" type="text" component={ renderInputField }/>
          <Field onChange={this._onImagePreviewChange.bind(this)} previewUrl={ imagePreviewUrl } name="image" label="Image" component={ renderDropzoneField } />
          <button type="submit" className="btn btn-primary">Create</button>
          <button type="button" className="btn btn-primary" onClick={this.props.onClose}>Cancel</button>
        </form>
        { this.state.error ? <span>{this.state.error}</span> : <noscript/> }
      </div>
    );
  }
}

export default connect(state => ({
  inputErrors: getFormSyncErrors('NewExpense')(state)
}), { createExpense })(reduxForm({
  validate,
  form:'NewExpense',
  validators
})(NewExpense));

I want to have access to the field-level errors of the form inside _onImagePreviewChange, with inputErrors I set inside mapStateToProps.

The problem is that the validation functions runs after onChange handler function. Therefore the error message is always stale. For example, if user select a file of disallowed type, the message would be undefined, the next time if user selects an file of allowed type, this time it wouldn't be undefined as an error was assigned to it in the previous step.

Why does this happen and how should I fix it?

回答1:

Validation can only run after some fields have changed and this is why the first time _onImagePreviewChange is called with an invalid value, there are still no validation errors (as you said, validation is one step behind).

I would suggest to use componentWillReceiveProps method and within this method access the next and the current validation errors, check if some error was fixed or generated and take the actions you need to take. For example:

 componentWillReceiveProps(newProps) {
    // here newProps.inputErrors is up-to-date while this.props.inputErrors is the current
    if (newProps.inputErrors && newProps.inputErrors !== this.props.inputErrors) {
      console.log(newProps.inputErrors); // up-to-date input errors
      if (newProps.inputErrors.image) {
        // clear invalid image field if you need to
        this.props.change('image', '');
      }
    }
  }