29

redux-form是一个非常引人注目的库,用于为 react 应用程序中的表单提供 redux 绑定,它应该非常方便。不幸的是,使用图书馆自己的例子,我没有真正绑定任何东西,这非常不方便。

我试图利用项目站点上的示例代码,并发现多个障碍,尽管试图忠实地重现它。我在哪里误解了这个 API?自编写演示代码以来,API 是否发生了变化?我是否错过了一些重要且明显的 redux 知识?

问题1:handleSubmit 方法的签名应该是handleSubmit(data)。但是 handleSubmit 目前只接收来自提交动作的 React 合成事件,没有数据。(事实上​​,使用编写的示例发送两个单独的事件,似乎是因为onSubmit表单和onClick按钮上的堆叠操作。)数据应该来自哪里,为什么我没有传递它给处理程序?

问题 2fields :必须在表单父级上定义一个关键对象,并作为道具提供给您的表单。fields不幸的是,文档中没有解释该对象的形状,也没有真正解释它的用途。它本质上是初始的“状态”对象吗?一个简单的 redux-form 对象容器在运行时用于错误等?我已经通过将道具与fields中的字段名称匹配来停止出错connectReduxForm,但由于数据没有绑定,我假设它的形状不正确。

问题 3:这些字段应该自动绑定到 and 的处理程序onBluronChange以便它们适当地更新存储。那永远不会发生。(感谢 Redux 开发工具,我们可以看到这一点。但是,handleSubmit它成功地调度了initializeaction,这表明 store、reducer 和其他基本管道都在工作。)

问题 4validateContact在初始化时触发一次,但再也不会触发。

不幸的是,这对于一个简单的 Fiddle 来说太复杂了,但是整个 repo(它只是基本的 ReduxStarterApp,加上这种形式的 POC)都可以在这里找到

而且,这是外部组件:

import React       from 'react';
import { connect } from 'react-redux';
import {initialize} from 'redux-form';

import ContactForm from '../components/simple-form/SimpleForm.js';

const mapStateToProps = (state) => ({
  counter : state.counter
});
export class HomeView extends React.Component {
  static propTypes = {
    dispatch : React.PropTypes.func.isRequired,
    counter  : React.PropTypes.number
  }

  constructor () {
    super();
  }
  handleSubmit(event, data) {
    event.preventDefault();
    console.log(event); // this should be the data, but is an event
    console.log(data); // no data here, either...
    console.log('Submission received!', data);
    this.props.dispatch(initialize('contact', {})); // clear form: THIS works
    return false;
  }

  _increment () {
    this.props.dispatch({ type : 'COUNTER_INCREMENT' });
  }


  render () {
    const fields = {
      name: '',
      address: '',
      phone: ''
    };

    return (
      <div className='container text-center'>
        <h1>Welcome to the React Redux Starter Kit</h1>
        <h2>Sample Counter: {this.props.counter}</h2>
        <button className='btn btn-default'
                onClick={::this._increment}>
          Increment
        </button>
        <ContactForm handleSubmit={this.handleSubmit.bind(this)} fields={fields} />
      </div>
    );
  }
}

export default connect(mapStateToProps)(HomeView);

以及内部表单组件:

import React, {Component, PropTypes} from 'react';
import {connectReduxForm} from 'redux-form';

function validateContact(data) {
  console.log("validating");
  console.log(data);
  const errors = {};
  if (!data.name) {
    errors.name = 'Required';
  }
  if (data.address && data.address.length > 50) {
    errors.address = 'Must be fewer than 50 characters';
  }
  if (!data.phone) {
    errors.phone = 'Required';
  } else if (!/\d{3}-\d{3}-\d{4}/.test(data.phone)) {
    errors.phone = 'Phone must match the form "999-999-9999"';
  }
  return errors;
}

class ContactForm extends Component {
  static propTypes = {
    fields: PropTypes.object.isRequired,
    handleSubmit: PropTypes.func.isRequired
  }

  render() {
    const { fields: {name, address, phone}, handleSubmit } = this.props;
    return (
      <form onSubmit={handleSubmit}>
        <label>Name</label>
        <input type="text" {...name}/>     {/* will pass value, onBlur and onChange */}
        {name.error && name.touched && <div>{name.error}</div>}

        <label>Address</label>
        <input type="text" {...address}/>  {/* will pass value, onBlur and onChange*/}
        {address.error && address.touched && <div>{address.error}</div>}

        <label>Phone</label>
        <input type="text" {...phone}/>    {/* will pass value, onBlur and onChange */}
        {phone.error && phone.touched && <div>{phone.error}</div>}

        <button type='submit'>Submit</button>
      </form>
    );
  }
}

// apply connectReduxForm() and include synchronous validation
ContactForm = connectReduxForm({
  form: 'contact',                      // the name of your form and the key to
                                        // where your form's state will be mounted
  fields: ['name', 'address', 'phone'], // a list of all your fields in your form
  validate: validateContact             // a synchronous validation function
})(ContactForm);

// export the wrapped component
export default ContactForm;
4

2 回答 2

23

connectReduxForm用另一个组件包装您的组件,该组件处理传入fieldsandhandleSubmit道具,但是您通过自己传递它们来将它们吹走。

试试这个(将道具重命名为onSubmit):

<ContactForm onSubmit={this.handleSubmit.bind(this)}/>

在中ContactForm将您自己的提交处理程序传递给handleSubmitredux-form 提供的函数

<form onSubmit={handleSubmit(this.props.onSubmit)}>

我建议使用React 开发人员工具来更好地了解正在发生的事情 - 你会看到 redux-form 如何包装你的组件并传递一大堆道具,如其 README中所述。

React 开发者工具中的 redux-form 组合

于 2015-10-15T08:14:37.553 回答
8

感谢 Jonny Buchanan,他涵盖了最重要的一点:不要像我那样做,并自动假设如果您的组件中需要 props,您必须自己提供它们。高阶函数的全部意义在于connectReduxForm在包装组件中提供它们。修复它立即给了我事件处理程序,除了提交之外的所有内容。

另一个重要的疏忽在这里:

注意 – 如果您没有自己执行 connect()(建议您不要这样做,除非您有需要它的高级用例),您必须在form中安装 reducer 。

我没有抓住重点。但是,实现在这里:

import { createStore, combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
const reducers = {
  // ... your other reducers here ...
  form: formReducer           // <---- Mounted at 'form'
}
const reducer = combineReducers(reducers);
const store = createStore(reducer);

formReducer 不能在 处引用formReducer,但需要语法form: formReducer。这是正确启用的更正handleSubmit

于 2015-10-18T15:04:47.770 回答