Redux 表单:如果验证成功则更改其他字段

Redux form: change other field if validation success

我正在使用 React Redux、Redux 形式和重新选择库 (https://github.com/reactjs/reselect)。

我有一个包含两个字段的组件:quotaamount。我想根据 quota 字段更新 amount 字段。对于 amount 的计算,我使用 selectorreselect

我只想更新 amount 字段,前提是它有效 quota

我试过没有成功 onChange field props 因为它在选择器之前执行。

我找到的最佳解决方案是使用 onChange reduxForm() 属性 因为它是在选择器之后执行的,但我无法在验证后 运行

这是我的代码:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { makeModalData } from '../selectors/Modal';
import { change, Field, reduxForm, formValueSelector } from 'redux-form';

const validate = values => {
  const errors = {};
  
  if (values.quota <= 1) {
    errors.quota = "The quota must be greater than 1";
  } else if (values.quota > 50) {
    errors.quota = "The quota must be less than 50";
  }
  
  return errors;
}

const updateForm = (values, dispatch, props, previousValues) => { 
  if (props.valid) { // this not work because it shows the previous state of validation
    if (values.quota !== previousValues.quota) { 
      let amount = props.modal_data.amount; // data with reselect 
      dispatch(change('modal', 'amount', amount));
    }
  }
  
}

class Modal extends Component {

  constructor(props) {
    super(props);
  }
  
  renderField = ({ input, label, type, meta: { touched, error } }) => (

    <div>
      <input {...input} type={type} placeholder={label} />
      {touched && error && <span>{error}</span>}
    </div>
  )
  
  // this not work because is executed before selector
  /*
  updateAmount(event) {    
    let amount = this.props.modal_data.amount;
    this.props.dispatch(change('modal', 'amount', amount));    
  }*/
  
  render() {
    return (
      <label>Quota</label>
      <Field
        name="quota"
        component={this.renderField}
        type="number"
        label="Quota"        
        /*onChange={this.updateAmount.bind(this)} */
      />
      <label>Amount</label>
      <Field
        name="amount"
        component={this.renderField}
        type="number"
        label="Amount"        
      />    
    )
  }
}

const makeMapStateToProps = () => {

  const modal_data = makeModalData(); // selector

  const mapStateToProps = state => {  
    
    let my_modal_data = modal_data(state, state.modal) 
    
    return {
      initialValues: {
        quota: state.modal.quota,
        amount: state.modal.amount,
      },      
    }
  }
  return mapStateToProps;
}

Modal = reduxForm({
  form: 'modal', 
  enableReinitialize: true,
  touchOnChange: true,
  validate,
  onChange: updateForm  
},makeMapStateToProps)(Modal);

Modal = connect(
  makeMapStateToProps,
  mapDispatchToProps
)(Modal);

export default Modal;

Here's a working CodeSandbox.

答案是使用 formValueSelector 并在 componentWillReceiveProps(或 componentDidUpdate)中更新。这是工作的版本 Modal.js。请注意,quota 验证在验证函数和计算中都被重复使用。

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

const validateQuota = quota => {
  if (quota <= 1) {
    return 'The quota must be greater than 1'
  } else if (quota > 50) {
    return 'The quota must be less than 50'
  }
}

const validate = values => {
  const errors = {}
  errors.quota = validateQuota(values.quota)
  return errors
}

/**
 * Arbitrary function that takes quota and calcuates amount.
 * For the purposes of this demo, I'm assuming that we're a
 * mobile phone company and are charging .95 per 5 GB of quota.
 */
const calculateAmount = quota => Math.ceil(quota / 5) * 19.95

class Modal extends Component {
  renderField = ({ input, label, type, meta: { touched, error } }) => (
    <div>
      <input {...input} type={type} placeholder={label} />
      {touched && error && <span>{error}</span>}
    </div>
  )

  componentWillReceiveProps(nextProps) {
    const { change, quota } = nextProps
    if (this.props.quota !== nextProps.quota && !validateQuota(quota)) {
      // quota value is valid
      change('amount', calculateAmount(quota))
    }
  }

  render() {
    return (
      <div>
        <label>Quota</label>
        <Field
          name="quota"
          component={this.renderField}
          type="number"
          label="Quota"
        />
        <label>Amount</label>
        <Field
          name="amount"
          component={this.renderField}
          type="number"
          label="Amount"
        />
      </div>
    )
  }
}

const valueSelector = formValueSelector('modal') // <-- form name

const makeMapStateToProps = () => {
  const mapStateToProps = state => {
    return {
      quota: valueSelector(state, 'quota'),
      initialValues: {
        // Not implementing the modal reducer...
        // quota: state.modal.quota,
        // amount: state.modal.amount
      }
    }
  }
  return mapStateToProps
}

Modal = reduxForm(
  {
    form: 'modal',
    enableReinitialize: true,
    validate
  },
  makeMapStateToProps
)(Modal)

const mapDispatchToProps = undefined // not included in Whosebug snippet

Modal = connect(makeMapStateToProps, mapDispatchToProps)(Modal)

export default Modal