redux-form は、reactアプリケーションのフォームにreduxバインディングを提供するための非常に魅力的なライブラリであり、非常に便利です。残念ながら、ライブラリの独自の例を使用すると、実際には何もバインドできません。これは非常に不便です。
プロジェクトサイトでサンプルコードを使用し、忠実に再現しようとしているにもかかわらず、複数の障害を見つけようとしています。このAPIをどこで誤解していますか?デモコードが作成されてからAPIは変更されましたか?重要な明白なリデュースの知識が欠けていますか?
問題1:handleSubmitメソッドの署名はhandleSubmit(data)
である必要があります。ただし、handleSubmitは現在、送信アクションからReact syntheticEventのみを受信しており、データは受信していません(実際、記述された例を使用すると、スタックされたonSubmit
アクションのために、フォーム上のボタンとボタン上のonClick
。)そのデータはどこから来ているのでしょうか。なぜハンドラーに渡さないのですか?
問題2:フォームの親で定義し、フォームにpropとして提供する必要がある重要なfields
オブジェクトがあります。残念ながら、そのfields
オブジェクトの形状は、ドキュメントでもその目的でも説明されていません。それは本質的に初期の「状態」オブジェクトですか?エラーなどのために実行時に使用するredux-formの単純なオブジェクトコンテナーですか? fields
の小道具をconnectReduxForm
のフィールド名に一致させることでエラーを停止しましたが、データがバインドされていないため、正しい形状ではないと仮定しています。
問題3:フィールドは、onBlur
およびonChange
のハンドラーに自動的にバインドされるため、ストアが適切に更新されます。それは決して起こらない。 (これはRedux開発ツールのおかげです。ただし、handleSubmit
はinitialize
アクションを正常にディスパッチしています。これは、ストア、レデューサー、およびその他の基本的な配管がすべて機能していることを示しています。)
問題4:validateContact
はinitで1回起動しますが、再び起動することはありません。
これは残念ながら単純なFiddleには複雑すぎますが、レポ全体(基本的な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;
connectReduxForm
は、fields
およびhandleSubmit
の小道具を渡すことを処理する別のコンポーネントでコンポーネントをラップしますが、それらを自分で渡すことでそれらを吹き飛ばしています。
代わりにこれを試してください(小道具の名前をonSubmit
に変更):
<ContactForm onSubmit={this.handleSubmit.bind(this)}/>
ContactForm
で、 redux-form が提供するhandleSubmit
関数に独自の送信ハンドラを渡します。
<form onSubmit={handleSubmit(this.props.onSubmit)}>
React開発者ツール を使用して、何が起こっているかをよりよく把握することをお勧めします-redux-formがコンポーネントをラップする方法と README で文書化されているように、小道具の束全体を渡します。
最も重要な点を説明してくれたJonny Buchananに感謝します。私がやったようにしないでください。コンポーネントに小道具が必要な場合は、自分で提供する必要があると自動的に仮定します。 connectReduxForm
である高階関数のポイントは、ラッパーコンポーネントで提供することです。これを修正すると、Submit以外のすべてのイベントハンドラーがすぐに得られました。
他の重要な監視はここにありました:
注–自分でconnect()を実行していない場合(必要な高度なユースケースがない限り、実行しないことをお勧めします)、formにリデューサーをマウントする必要があります。
私はその点を理解できませんでした。ただし、実装は次のとおりです。
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
を適切に有効化した修正でした。