Package Exports
- redux-form
- redux-form/lib/actions
- redux-form/lib/reducer
- redux-form/lib/reducer.js
- redux-form/lib/reduxForm
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (redux-form) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
#redux-form
redux-form
works with React Redux to enable an html form in
React to use Redux to store all of its state.
Table of Contents
- Installation
- Release Notes
- Benefits - Why use this library?
- Implementation Guide <-------------- Start here!
- A Simple Form Component
- ES7 Decorator Sugar - ⚠️ Experimental! ⚠️
- Synchronous Validation - Client Side
- Asynchronous Validation - Server Side
- Submitting Your Form
- Responding to Other Actions
- Normalizing Form Data
- Editing Multiple Records
- Calculating
props
from Form Data - Dynamic Forms
- Advanced Usage
- API
connectReduxForm(config)
reduxForm(config)
reducer
reducer.plugin(Object<String, Function>)
reducer.normalize(Object<String, Function>)
props
passed into your decorated component - props provided byredux-form
props
you can pass into your decorated component - extra functionality!- Action Creators - Advanced
- Working Demo
Installation
npm install --save redux-form
Release Notes
This project follows SemVer and each release is posted on the Release Notes page.
Benefits
Why would anyone want to do this, you ask? React a perfectly good way of keeping state in each component! The reasons are threefold.
Unidirectional Data Flow
For the same reason that React and Flux is superior to Angular's bidirectional data binding. Tracking down bugs is much simpler when the data all flows through one dispatcher.
Redux Dev Tools
When used in conjunction with Redux Dev Tools, you can fast forward and rewind through your form data entry to better find bugs.
Stateless Components
By removing the state from your form components, you inherently make them easier to understand, test, and debug.
The React philosophy is to always try to use props
instead of state
when possible.
Implementation Guide
STEP 1: The first thing that you have to do is to give the redux-form
reducer to Redux. You will only have to do
this
once, no matter how many form components your app uses.
import { createStore, combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
const reducers = {
// ... your other reducers here ...
form: formReducer // <---- Mounted at 'form'. See note below.
}
const reducer = combineReducers(reducers);
const store = createStore(reducer);
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
.
STEP 2: Wrap your form component with connectReduxForm()
. connectReduxForm()
wraps your form component in a
Higher Order Component that connects to the Redux store and provides functions, as props to your component, for your
form elements to use for sending onChange
and onBlur
events, as well as a function to handle synchronous
validation onSubmit
. Let's look at a simple example.
A Simple Form Component
You will need to wrap your form component with redux-form
's connectReduxForm()
function.
IMPORTANT: If you are using
redux-form
withreact-native
, you will need to usereduxForm()
instead ofconnectReduxForm()
, at least until React 0.14 is released.
import React, {Component, PropTypes} from 'react';
import {connectReduxForm} from 'redux-form';
import validateContact from './validateContact';
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 onClick={handleSubmit}>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;
Notice that we're just using vanilla <input>
elements there is no state in the ContactForm
component.
handleSubmit
will call the function passed into ContactForm
's onSubmit
prop, if
and only if the synchronous validation passes. See Submitting Your Form.
ES7 Decorator Sugar
Using ES7 decorator proposal, the example above could be written as:
@connectReduxForm({
form: 'contact',
fields: ['name', 'address', 'phone'],
validate: validateContact
})
export default class ContactForm extends Component {
Much nicer, don't you think?
You can enable it with Babel Stage 1. Note that decorators are experimental, and this syntax might change or be removed later.
Synchronous Validation
You may optionally supply a validation function, which is in the form ({}) => {}
and takes in all
your data and spits out error messages. Your error messages may be strings or arrays of strings (if your field data
is complex). For example:
function validateContact(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;
}
You get the idea.
Asynchronous Validation
Async validation can be achieved by calling an additional function on the function returned by
connectReduxForm()
and passing it an asynchronous function that returns a promise that will resolve
to validation errors of the format that the synchronous validation function
generates. So this...
// apply connectReduxForm() and include synchronous validation
ContactForm = connectReduxForm({
form: 'contact',
fields: ['name', 'address', 'phone'],
validate: validateContact
})(ContactForm);
...changes to this:
function validateContactAsync(data) {
return new Promise((resolve, reject) => {
const errors = {};
// do async validation
resolve(errors);
});
}
// apply connectReduxForm() and include synchronous AND asynchronous validation
ContactForm = connectReduxForm({
form: 'contact',
fields: ['name', 'address', 'phone'],
validate: validateContact,
asyncValidate: validateContactAsync
})(ContactForm);
Optionally, if you want asynchronous validation to be triggered when one or more of your form
fields is blurred, you may pass those fields to the async()
function along with the asynchronous
validation function. Like so:
// will only run async validation when 'name' or 'phone' is blurred
ContactForm = connectReduxForm({
form: 'contact',
fields: ['name', 'address', 'phone'],
validate: validateContact,
asyncValidate: validateContactAsync,
asyncBlurFields: ['name', 'phone']
})(ContactForm);
With that call, the asynchronous validation will be called when either name
or phone
is blurred.
Assuming that they have their onBlur={handleBlur('name')}
properties properly set up.
NOTE! If you only want asynchronous validation, you may leave out the synchronous validation function. And if you only want it to be run on submit, you may leave out the async blur fields, as well.
ContactForm = connectReduxForm({
form: 'contact',
fields: ['name', 'address', 'phone'],
asyncValidate: validateContactAsync
})(ContactForm);
Submitting Your Form
The recommended way to submit your form is to create your form component as shown above,
using the handleSubmit
prop, and then pass an onSubmit
prop to your form component.
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {initialize} from 'redux-form';
class ContactPage extends Component {
static propTypes = {
dispatch: PropTypes.func.isRequired
}
handleSubmit(data) {
console.log('Submission received!', data);
this.props.dispatch(initialize('contact', {})); // clear form
}
render() {
return (
<div>
<h1>Contact Information</h1>
<ContactForm onSubmit={this.handleSubmit.bind(this)}/>
</div>
);
}
}
export default connect()(ContactPage); // adds dispatch prop
Or, if you wish to do your submission directly from your decorated form component, you may pass a function
to handleSubmit
. To abbreviate the example shown above:
class ContactForm extends Component {
static propTypes = {
// ...
handleSubmit: PropTypes.func.isRequired
}
saveForm(data) {
// make server call to save the data
}
render() {
const {
// ...
handleSubmit
} = this.props;
return (
<form onSubmit={handleSubmit(this.saveForm)}> // <--- pass saveForm
// ...
</form>
);
}
}
Responding to Other Actions
Part of the beauty of the flux architecture is that all the reducers (or "stores", in canonical Flux terminology) receive all the actions, and they can modify their data based on any of them. For example, say you have a login form, and when your login submission fails, you want to clear out the password field. Your login submission is part of another reducer/actions system, but your form can still respond.
Rather than just using the vanilla reducer from redux-form
, you can augment it to do other things by calling
the plugin()
function.
import {reducer as formReducer} from 'redux-form';
import {AUTH_LOGIN_FAIL} from '../actions/actionTypes';
const reducers = {
// ... your other reducers here ...
form: formReducer.plugin({
login: (state, action) => { // <------ 'login' is name of form given to connectReduxForm()
switch(action.type) {
case AUTH_LOGIN_FAIL:
return {
...state,
password: {} // <----- clear password field
};
default:
return state;
}
}
})
}
const reducer = combineReducers(reducers);
const store = createStore(reducer);
Normalizing Form Data
Let's say that you have a form field that only accepts uppercase letters and another one where you want the value to
be formatted in the 999-999-9999
United States phone number format. redux-form
gives you a way to normalize your
data on every action to the reducer by calling the normalize()
function on the default reducer.
import {reducer as formReducer} from 'redux-form';
const reducers = {
// ... your other reducers here ...
form: formReducer.normalize({
contact: { // <--- the form name
licensePlate: (value, previousValue, allValues) => // <--- field normalizer
value && value.toUpperCase(),
phone: (value, previousValue, allValues) => { // <--- field normalizer
if (value) {
const match = value.match(/(\d{3})-?(\d{3})-?(\d{4})/);
if (match) {
return `${match[1]}-${match[2]}-${match[3]}`;
}
}
return value;
}
}
})
}
const reducer = combineReducers(reducers);
const store = createStore(reducer);
Editing Multiple Records
Editing multiple records on the same page is trivially easy with redux-form
. All you have to do is to pass a
unique formKey
and initialValues
props into your form element. Let's say we want to edit many contacts on the
same page.
import React, {Component, PropTypes} from 'react';
import ContactForm from './ContactForm';
export default class ContactsPage extends Component {
static propTypes = {
contacts: PropTypes.array.isRequired // how you pass the data in is up to you
}
handleSubmit(id, data) {
// send to server
}
render() {
const {contacts} = this.props;
return (
<div>
{contacts.map(function (contact) {
return <ContactForm
key={contact.id} // required by react
formKey={String(contact.id)} // required by redux-form
initialValues={contact} // initialize form values
onSubmit={this.handleSubmit.bind(this, contact.id)}/>
})}
</div>
);
}
}
Calculating props
from Form Data
You may want to have some calculated props, perhaps using reselect
selectors based on the values of the data in your form. You might be tempted to do this in the mapStateToProps
given to connect()
. This will not work. The reason is that the form contents in the Redux store are lazily
initialized, so state.form.contacts.data.name
will fail, because state.form.contacts
will be undefined
until the
first form action is dispatched.
The recommended way to accomplish this is to use yet another Higher Order Component decorator, such as
map-props
, like so:
import mapProps from 'map-props';
...
// FIRST map props
ContactForm = mapProps({
hasName: props => !!props.name.value
hasPhone: props => !!props.phone.value
})(ContactForm);
// THEN apply connectReduxForm() and include synchronous validation
ContactForm = connectReduxForm({
form: 'contact',
fields: ['name', 'address', 'phone'],
validate: validateContact
})(ContactForm);
...
Or, in ES7 land...
javascript @connectReduxForm({ form: 'contact', fields: ['name', 'address', 'phone'], validate: validateContact }) @mapProps({ hasName: props => !!props.name.value hasPhone: props => !!props.phone.value }) export default class ContactForm extends Component {
Dynamic Forms
What if you don't know the shape of your form, i.e. what fields are needed, until runtime, maybe based on some data
retrieved from the server? No problem! Because of the functional nature of React, Redux and redux-form
, you can
define and decorate your form component at runtime.
import React, {Component, PropTypes} from 'react';
import {connectReduxForm} from 'redux-form';
class DynamicForm extends Component {
static propTypes = {
formName: PropTypes.string.isRequired,
fieldsNeeded: PropTypes.arrayOf(PropTypes.string).isRequired
}
render() {
const {formName, fieldsNeeded} = this.props;
// define form class
class Form extends Component {
static propTypes = {
fields: PropTypes.object.isRequired
}
render() {
const {fields} = this.props; // passed in by redux-form
return (
<div>
{Object.keys(fields).map(fieldName => <div key={fieldName}>
<label>{fieldName}</label>
<input type="text" {...fields[fieldName]}/>
</div>)}
</div>
);
}
}
// connect Form to Redux and decorate with redux-form
Form = connectReduxForm({ form: formName, fields: fieldsNeeded })(Form);
// render connected and decorated form
return <Form/>;
}
}
Advanced Usage
Doing the connect()
ing Yourself
If, for some reason, you cannot mount the redux-form
reducer at form
in Redux, you may mount it anywhere else and
do the connect()
call yourself. Rather than wrap your form component with redux-form
's connectReduxForm()
, you
will need to wrap your form component both with
React Redux's connect()
function and with redux-form
's
reduxForm()
function.
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import reduxForm from 'redux-form';
import validateContact from './validateContact';
class ContactForm extends Component {
//...
}
// apply reduxForm() and include synchronous validation
// note: we're using reduxForm, not connectReduxForm
ContactForm = reduxForm({
form: 'contact',
fields: ['name', 'address', 'phone'],
validate: validateContact
})(ContactForm);
// ------- HERE'S THE IMPORTANT BIT -------
function mapStateToProps(state, ownProps) {
// this is React Redux API: https://github.com/rackt/react-redux
// for example, you may use ownProps here to refer to the props passed from parent.
return {
form: state.placeWhereYouMountedFormReducer[ownProps.something]
};
}
// apply connect() to bind it to Redux state
ContactForm = connect(mapStateToProps)(ContactForm);
// export the wrapped component
export default ContactForm;
As you can see, connectReduxForm()
is a tiny wrapper over reduxForm()
that applies connect()
for you.
Binding Action Creators
When doing the connect()
ing yourself, if your form component also needs other redux action creators - and you will
if you are performing your server submit in your form component - you cannot simply use the default
bindActionCreators()
from redux
, because that will remove dispatch
from the props the connect()
passes
along, and reduxForm()
needs dispatch
. You will need to also include dispatch
in your mapDispatchToProps()
function. So change this...
import {bindActionCreators} from `redux`;
...
function mapDispatchToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch);
}
ContactForm = connect(
mapStateToProps,
mapDispatchToProps
)(ContactForm);
...to...
import {bindActionCreators} from `redux`;
...
function mapDispatchToProps(dispatch) {
return {
...bindActionCreators(actionCreators, dispatch),
dispatch // <----- passing dispatch, too
};
}
ContactForm = connect(
mapStateToProps,
mapDispatchToProps
)(ContactForm);
API
connectReduxForm(config:Object)
-config.form : string
[optional]
the name of your form and the key to where your form's state will be mounted, under the
redux-form
reducer, in the Redux store. If you do not provide this, you must pass it in as aformName
prop to your component.
-config.fields : Array<string>
a list of all your fields in your form. This is used for marking all of the fields as
touched
on submit.
-config.validate : Function
[optional]
your synchronous validation function. Defaults to
() => ({})
-config.readonly : boolean
[optional]
if
true
, the decorated component will not be passed any of thehandleX
oronX
props that will allow it to mutate the state. Useful for decorating another component that is not your form, but that needs to know about the state of your form.
-config.touchOnBlur : boolean
[optional]
marks fields to touched when the blur action is fired. Defaults to
true
-config.touchOnChange : boolean
[optional]
marks fields to touched when the change action is fired. Defaults to
false
-config.asyncValidate : Function
[optional]
a function that takes all the form data and returns a Promise that will resolve to an object of validation errors in the form
{ field1: <string>, field2: <string>, valid: <boolean> }
just like the synchronous validation function. See Aynchronous Validation for more details.
-config.asyncBlurFields : Array<String>
[optional]
field names for which
handleBlur
should trigger a call to theasyncValidate
function. Defaults to[]
.
reduxForm(config:Object)
[NOT RECOMMENDED]
reduxForm()
has the same API asconnectReduxForm()
except that you must wrap the component inconnect()
yourself.
reducer
The form reducer. Should be given to mounted to your Redux state at
form
.
reducer.plugin(Object<String, Function>)
Returns a form reducer that will also pass each action through additional reducers specified. The parameter should be an object mapping from
formName
to a(state, action) => nextState
reducer. Thestate
passed to each reducer will only be the slice that pertains to that form. See Responding to Other Actions.
reducer.normalize(Object<String, Object<String, Function>>)
Returns a form reducer that will also pass each form value through the normalizing functions provided. The parameter is an object mapping from
formName
to an object mapping fromfieldName
to a normalizer function. The normalizer function is given three parameters and expected to return the normalized value of the field. See Normalizing Form Data.
-value : string
The current value of the field
-previousValue : string
The previous value of the field before the current action was dispatched
-allValues : Object<string, string>
All the values of the current form
props
passed into your decorated component
The props passed into your decorated component by redux-form
will be:
-active : String
the name of the currently active (with focus) field
-asyncValidate : Function
a function that may be called to initiate asynchronous validation if asynchronous validation is enabled
-asyncValidating : boolean
true
if the asynchronous validation function has been called but has not yet returned.
-dirty : boolean
true
if the form data has changed from its initialized values. Opposite ofpristine
.
-fields : Object
The form data, in the form
{ field1: <Object>, field2: <Object> }
, where each fieldObject
has the following properties:
---active : boolean
true
if this field currently has focus. It will only work if you are passinghandleFocus
to your input element.
---checked : boolean?
An alias for
value
only whenvalue
is a boolean. Provided for convenience of destructuring the whole field object into the props of a form element.
---dirty : boolean
true
if the field value has changed from its initialized value. Opposite ofpristine
.
---error : String?
The error for this field if its value is not passing validation. Both synchronous and asynchronous validation errors will be reported here.
---handleBlur : Function
A function to call when the form field loses focus. It expects to receive the React SyntheticEvent and is meant to be passed to the form element's
onBlur
prop. Alternatively: you may pass the value directly intohandleBlur
to set the value on in the form.
---handleChange : Function
A function to call when the form field is changed. It expects to receive the React SyntheticEvent and is meant to be passed to the form element's
onChange
prop. Alternatively: you may pass the value directly intohandleChange
to set the value on in the form.
---handleFocus : Function
A function to call when the form field receives focus. It is meant to be passed to the form element's
onFocus
prop.
---invalid : boolean
true
if the field value fails validation (has a validation error). Opposite ofvalid
.
---name : String
The name of the field. It will be the same as the key in the
fields
Object, but useful if bundling up a field to send down to a specialized input component.
---onBlur : Function
An alias for
handleBlur
. Provided for convenience of destructuring the whole field object into the props of a form element.
---onChange : Function
An alias for
handleChange
. Provided for convenience of destructuring the whole field object into the props of a form element.
---onFocus : Function
An alias for
handleFocus
. Provided for convenience of destructuring the whole field object into the props of a form element.
---pristine : boolean
true
if the field value is the same as its initialized value. Opposite ofdirty
.
---touched : boolean
true
if the field has been touched.
---valid : boolean
true
if the field value passes validation (has no validation errors). Opposite ofinvalid
.
---value: any
The value of this form field. It will be a boolean for checkboxes, and a string for all other input types.
---visited: boolean
true
if this field has ever had focus. It will only work if you are passinghandleFocus
to your input element.
-handleBlur(field:string, value:any?) : Function
Returns a
handleBlur
function for the field passed.handleBlur('age')
returnsfields.age.handleBlur
. If you pass it both a field and a value, it will set that value in the form. If you give it just a field, it will return a function that will set the value when either given the value or an event from an input.
-handleChange(field:string, value:any?) : Function
Returns a
handleChange
function for the field passed.handleChange('age')
returnsfields.age.handleChange
. If you pass it both a field and a value, it will set that value in the form. If you give it just a field, it will return a function that will set the value when either given the value or an event from an input.
-handleFocus(field:string) : Function
Returns a
handleFocus
function for the field passed.handleFocus('age')
returnsfields.age.handleFocus
, a function to be passsed to theonFocus
prop of your input component.
-handleSubmit : Function
a function meant to be passed to
<form onSubmit={handleSubmit}>
or to<button onClick={handleSubmit}>
. It will run validation, both sync and async, and, if the form is valid, it will callthis.props.onSubmit(data)
with the contents of the form data.
Optionally, you may also pass your
onSubmit
function tohandleSubmit
which will take the place of theonSubmit
prop. For example:<form onSubmit={handleSubmit(this.save.bind(this))}>
If your
onSubmit
function returns a promise, thesubmitting
property will be set totrue
until the promise has been resolved or rejected. If it is rejected with an object matching{ field1: 'error', field2: 'error' }
then the submission errors will be added to each field (to theerror
prop) just like async validation errors are.
-initializeForm(data:Object) : Function
Initializes the form data to the given values. All
dirty
andpristine
state will be determined by comparing the current data with these initialized values.
-invalid : boolean
true
if the form has validation errors. Opposite ofvalid
.
-pristine: boolean
true
if the form data is the same as its initialized values. Opposite ofdirty
.
-resetForm() : Function
Resets all the values in the form to the initialized state, making it pristine again.
-formKey : String
The same
formKey
prop that was passed in. See Editing Multiple Records.
-submitting : boolean
Whether or not your form is currently submitting. This prop will only work if you have passed an
onSubmit
function that returns a promise. It will be true until the promise is resolved or rejected.
-touch(...field:string) : Function
Marks the given fields as "touched" to show errors.
-touchAll() : Function
Marks all fields as "touched" to show errors. should be called on form submission.
-untouch(...field:string) : Function
Clears the "touched" flag for the given fields
-untouchAll() : Function
Clears the "touched" flag for the all fields
-valid : boolean
true
if the form passes validation (has no validation errors). Opposite ofinvalid
.
-values : Object
All of your values in the form
{ field1: <string>, field2: <string> }
.
props
you can pass into your decorated component
The props that you can pass into decorated component by redux-form
will be:
-formKey : String
[optional]
a unique key for the subform this component will be editing. See Editing Multiple Records.
-formName : String
[optional]
the name of your form and the key to where your form's state will be mounted, under the
redux-form
reducer, in the Redux store. Will overwrite anyconfig.form
value that was passed toconnectReduxForm(config)
.
-initialValues : Object
[optional]
the values with which to initialize your form in
componentWillMount
. Particularly useful when Editing Multiple Records, but can also be used with single-record forms.
-onSubmit : Function
[optional]
the function to call with the form data when the
handleSubmit
is fired from within the form component. If you do not specify it as a prop here, you must pass it as a parameter tohandleSubmit
inside your form component.
If your
onSubmit
function returns a promise, thesubmitting
property will be set totrue
until the promise has been resolved or rejected. If it is rejected with an object matching{ field1: 'error', field2: 'error' }
then the submission errors will be added to each field (to theerror
prop) just like async validation errors are.
Action Creators
redux-form
exports all of its internal action creators, allowing you complete control to dispatch any action
you wish. However, it is highly recommended that you use the actions passed as props to your component
for most of your needs.
-blur(form:String, field:String, value:String)
Saves the value and, if you have
touchOnBlur
enabled, marks the field astouched
.
-change(form:String, field:String, value:String)
Saves the value and, if you have
touchOnChange
enabled, marks the field astouched
.
-focus(form:String, field:String)
Marks the given field as
active
andvisited
.
-initialize(form:String, data:Object)
Sets the initial values in the form with which future data values will be compared to calculate
dirty
andpristine
. Thedata
parameter should only containString
values.
-initializeWithKey(form:String, formKey, data:Object)
Used when editing multiple records with the same form component. See Editing Multiple Records.
-reset(form:String)
Resets the values in the form back to the values past in with the most recent
initialize
action.
-startAsyncValidation(form:String)
Flips the
asyncValidating
flagtrue
.
-startSubmit(form:String)
Flips the
submitting
flagtrue
.
-stopSubmit(form:String, errors:Object)
Flips the
submitting
flagfalse
and populatessubmitError
for each field.
-stopAsyncValidation(form:String, errors:Object)
Flips the
asyncValidating
flagfalse
and populatesasyncError
for each field.
-touch(form:String, ...fields:String)
Marks all the fields passed in as
touched
.
-untouch(form:String, ...fields:String)
Resets the 'touched' flag for all the fields passed in.
Working Demo
Check out the
react-redux-universal-hot-example project to see
redux-form
in action.
This is an extremely young library, so the API may change. Comments and feedback welcome.