redux-form est une bibliothèque très convaincante pour fournir des liaisons redux pour les formulaires dans une application React, ce qui devrait être super pratique. Malheureusement, en utilisant les propres exemples de la bibliothèque, je n'arrive pas à lier quoi que ce soit, ce qui est super pratique.
J'essaie d'utiliser l'exemple de code sur le site du projet et de trouver plusieurs obstacles, malgré une tentative de le reproduire fidèlement. Où est-ce que j'interprète mal cette API? L'API a-t-elle changé depuis l'écriture du code de démonstration? Suis-je en train de manquer un élément essentiel et évident de connaissances redux?
Problème 1 : la signature de la méthode handleSubmit doit être handleSubmit(data)
. Mais handleSubmit ne reçoit actuellement que le React synthétiqueEvent de l'action de soumission, et aucune donnée. (En fait, utiliser l'exemple tel qu'écrit envoyait deux événements distincts, apparemment à cause du _ onSubmit
action sur le formulaire et onClick
sur le bouton.) D'où ces données sont-elles censées provenir, et pourquoi ne parviens-je pas à les transmettre au gestionnaire?
Problème 2 : il y a un objet fields
critique qui doit être défini sur le parent du formulaire et fourni comme accessoire à votre formulaire. Malheureusement, la forme de cet objet fields
n'est pas vraiment expliquée dans les documents, ni son objectif. S'agit-il essentiellement de l'objet "état" initial? Un simple conteneur d'objets pour redux-form à utiliser lors de l'exécution pour les erreurs, etc? Je l'ai fait pour arrêter l'erreur en faisant correspondre les accessoires sur fields
aux noms de champ dans connectReduxForm
, mais parce que les données ne sont pas contraignantes, je suppose que ce n'est pas la bonne forme.
Problème 3 : Les champs sont censés être liés automatiquement aux gestionnaires pour onBlur
et onChange
, afin qu'ils mettre à jour le magasin de manière appropriée. Cela n'arrive jamais. (Ce que nous pouvons voir grâce aux outils de développement Redux. Cependant, handleSubmit
envoie avec succès l'action initialize
, ce qui suggère que le magasin, le réducteur et d'autres éléments de plomberie de base fonctionnent tous.)
Problème 4 : validateContact
se déclenche une fois sur init, mais plus jamais.
C'est malheureusement trop complexe pour un simple violon, mais le repo entier (c'est juste le ReduxStarterApp de base, plus ce formulaire POC) est disponible ici .
Et, voici le composant extérieur:
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);
Et le composant de formulaire interne:
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
encapsule votre composant avec un autre composant qui gère le passage des accessoires fields
et handleSubmit
, mais vous les faites exploser en les passant vous-même.
Essayez ceci à la place (renommé le prop en onSubmit
):
<ContactForm onSubmit={this.handleSubmit.bind(this)}/>
Et dans ContactForm
, passez votre propre gestionnaire de soumission à la fonction handleSubmit
fournie par redux-form :
<form onSubmit={handleSubmit(this.props.onSubmit)}>
Je recommande d'utiliser React developer tools pour avoir une meilleure idée de ce qui se passe - vous verrez comment redux-form encapsule votre composant et lui transmet tout un tas d'accessoires, comme documenté dans son README .
Merci à Jonny Buchanan, qui a couvert le point le plus important: ne faites pas comme moi et supposez automatiquement que si des accessoires sont requis dans votre composant, vous devez les fournir vous-même. L'intérêt de la fonction d'ordre supérieur qui est connectReduxForm
est de les fournir dans le composant wrapper. Correction qui m'a immédiatement donné des gestionnaires d'événements, pour tout sauf Soumettre.
L'autre surveillance critique était ici:
REMARQUE - Si vous n'effectuez pas la connexion () vous-même (et il est recommandé de ne pas le faire, sauf si vous avez un cas d'utilisation avancée qui l'exige), vous devez monter le réducteur à formulaire.
Je ne l'ai pas compris. Mais l'implémentation est là:
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);
Le formReducer ne peut pas être référencé à formReducer
, mais nécessite la syntaxe form: formReducer
. C'est la correction qui a correctement activé handleSubmit
.