Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wire up redux-form bindings to the form's inputs

redux-form is a very compelling library for providing redux bindings for forms in a react application, which should be super-convenient. Unfortunately, using the library's own examples, I'm failing to actually bind anything, which is super in-convenient.

I'm attempting to make use of the sample code on the project site, and finding multiple obstacles, despite attempting to reproduce it faithfully. Where am I misinterpreting this API? Has the API shifted since the demo code was written? Am I missing some critical and obvious piece of redux knowledge?

Problem 1: the signature for the handleSubmit method should be handleSubmit(data). But handleSubmit is currently receiving only the React syntheticEvent from the submit action, and no data. (In fact, using the example as-written was sending two separate events, seemingly because of the stacked onSubmit action on the form and the onClick on the button.) Where is that data supposed to be coming from, and why am I failing to pass it to the handler?

Problem 2: there's a critical fields object that must be defined on the form parent and supplied as prop to your form. Unfortunately, the shape of that fields object is not explained in the docs, nor its purpose, really. Is it essentially the initial 'state' object? A simple object container for redux-form to use at runtime for errors, etc? I've gotten it to stop erroring by matching the props on fields to the field names in connectReduxForm, but because the data isn't binding, I'm assuming it's not the right shape.

Problem 3: The fields are supposed to be auto-bound to handlers for onBlur and onChange, so that they update the store appropriately. That's never happening. (Which we can see thanks to the Redux dev-tools. However, handleSubmit is successfully dispatching the initialize action, which suggests the store, reducer, and other basic plumbing are all working.)

Problem 4: validateContact is firing once on init, but never again.

This is unfortunately too complex for a simple Fiddle, but the entire repo (it's just the basic ReduxStarterApp, plus this form POC) is available here.

And, here is the outer component:

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); 

And the inner form component:

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; 
like image 298
XML Avatar asked Oct 15 '15 01:10

XML


People also ask

How do I get input value in Redux?

I use ref to get the value of input, the handleSubmit() method is in the class, so it can get input by ref. If you want use the dispatch method, just map some props in mapDispatchToProps(). Show activity on this post.

How do I connect Redux to component?

The connect() function connects a React component to a Redux store. It provides its connected component with the pieces of the data it needs from the store, and the functions it can use to dispatch actions to the store.

Can you use Redux with hooks?

React Redux includes its own custom hook APIs, which allow your React components to subscribe to the Redux store and dispatch actions.


2 Answers

connectReduxForm wraps your component with another component which handles passing in the fields and handleSubmit props, but you're blowing those away by passing them in yourself.

Try this instead (renamed the prop to onSubmit):

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

And in ContactForm, pass your own submit handler to the handleSubmit function provided by redux-form:

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

I recommend using the React developer tools to get a better picture of what's going on - you'll see how redux-form wraps your component and passes it a whole bunch of props, as documented in its README.

redux-form composition in React developer tools

like image 144
Jonny Buchanan Avatar answered Oct 02 '22 13:10

Jonny Buchanan


Thanks to Jonny Buchanan, who covered the most important point: don't do as I did and automatically assume that if props are required in your component, you must need to provide them yourself. The whole point of the higher-order function that is connectReduxForm is to provide them in the wrapper component. Fixing that immediately gave me event-handlers, for everything except Submit.

The other critical oversight was here:

NOTE – If you are not doing the connect()ing yourself (and it is recommended that you do not, unless you have an advanced use case that requires it), you must mount the reducer at form.

I didn't catch the point of that. But, the implementation is here:

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); 

The formReducer can't be referenced at formReducer, but requires the syntax form: formReducer. This was the correction that properly enabled handleSubmit.

like image 27
XML Avatar answered Oct 02 '22 11:10

XML