2015-10-15 94 views
28

redux-form是一個非常引人注目的庫,用於爲反應應用程序中的表單提供redux綁定,這應該非常方便。不幸的是,使用圖書館自己的例子,我沒有真正綁定任何東西,這是超方便的。如何將redux-form綁定連接到表單的輸入

我試圖利用項目網站上的示例代碼,並發現多個障礙,儘管試圖忠實地重現它。我在哪裏誤解了這個API?自從演示代碼寫入以來API已經移動了嗎?我是否缺少一些關鍵和明顯的重要知識?

問題1:handleSubmit方法的簽名應該是handleSubmit(data)。但是handleSubmit目前只接收來自提交操作的React syntheticEvent,並且沒有數據。 (事實上​​,使用寫入的示例發送兩個單獨的事件,看起來是因爲表單上的onSubmit堆棧操作和按鈕上的onClick堆棧。)數據應該來自哪裏,爲什麼我無法訪問把它傳遞給處理程序?

問題2:有一個關鍵的fields對象必須在父窗體上定義並作爲prop提供給窗體。不幸的是,這個對象的形狀沒有在文檔中解釋,也沒有在其目的中解釋。它基本上是最初的'狀態'對象嗎?用於redux-form的簡單對象容器,以便在運行時用於錯誤等等?我已經通過將fields上的道具匹配到connectReduxForm中的字段名稱來阻止錯誤,但由於數據不具有約束力,我假設它不是正確的形狀。

問題3:各字段應該是自動結合到處理程序onBluronChange,以使它們適當地更新存儲。這從來沒有發生過。 (我們可以看到多虧了終極版開發工具然而,handleSubmit成功分派initialize行動,這表明實體店,減速機等基本的管道都在努力。)

問題4validateContact是初始化時會觸發一次,但不會再次發生。

這對一個簡單的小提琴來說太複雜了,但是整個回購(它只是基本的ReduxStarterApp,加上這種形式的POC)is available here

而且,這裏是外部組件:

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; 
+1

謝謝你這個精心製作的問題!我遇到同樣的困難,我發現複雜形式的文檔和例子錯綜複雜。在你和@Jonny Buchanan的幫助下,我能夠繼續進行(雖然我有一個複雜的數據結構,但我還沒有完成)。添加[redux devtools擴展](https://github.com/zalmoxisus/redux-devtools-extension)也對調試有很大幫助! –

回答

23

connectReduxForm用它處理傳入fieldshandleSubmit道具另一個組件包裝的成分,但你通過將它們傳入自己而將它們吹走。

,而不是嘗試這種(改名道具onSubmit):

​​

而且在ContactFormpass your own submit handler to the handleSubmit function provided by redux-form

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

我建議使用React developer tools得到什麼回事的更好的畫面 - 你會看到redux-form如何包裝你的組件和passes it a whole bunch of props, as documented in its README

redux-form composition in React developer tools

+2

你能更新鏈接嗎?所有鏈接現在指向'redux-form' github主頁。 – Mithril

8

多虧強尼·布坎南,誰涵蓋了最重要的一點:不要做像我一樣,並自動假設,如果在你的組件需要的道具,你必須要自己提供。高階函數connectReduxForm的整個要點是在包裝器組件中提供它們。修復,立即給我事件處理程序,除了提交的一切。

的其他重要監督在這裏:

注意 - 如果不這樣做的connect()荷蘭國際集團自己(和它 建議你不這樣做,除非你有一個先進的使用情況 要求它),則必須將減速機安裝在的形式

我沒有理解這一點。但是,執行是在這裏:

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的更正。