Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
redux-form
Advanced tools
redux-form is a library that allows you to manage form state in Redux. It provides a way to keep form state in a Redux store, making it easier to manage complex forms and their validation, submission, and other behaviors.
Form Creation
This code demonstrates how to create a simple form using redux-form. The `Field` component is used to define form fields, and the `reduxForm` higher-order component is used to connect the form to Redux.
import React from 'react';
import { Field, reduxForm } from 'redux-form';
let SimpleForm = props => {
const { handleSubmit } = props;
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="firstName">First Name</label>
<Field name="firstName" component="input" type="text" />
</div>
<button type="submit">Submit</button>
</form>
);
};
SimpleForm = reduxForm({
form: 'simple'
})(SimpleForm);
export default SimpleForm;
Form Validation
This code demonstrates how to add validation to a form using redux-form. The `validate` function checks for errors and returns an object with error messages, which are then used by redux-form to display validation errors.
import React from 'react';
import { Field, reduxForm } from 'redux-form';
const validate = values => {
const errors = {};
if (!values.firstName) {
errors.firstName = 'Required';
}
return errors;
};
let ValidatedForm = props => {
const { handleSubmit } = props;
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="firstName">First Name</label>
<Field name="firstName" component="input" type="text" />
</div>
<button type="submit">Submit</button>
</form>
);
};
ValidatedForm = reduxForm({
form: 'validated',
validate
})(ValidatedForm);
export default ValidatedForm;
Form Submission
This code demonstrates how to handle form submission using redux-form. The `handleSubmit` function is passed a `submit` function that processes the form data.
import React from 'react';
import { Field, reduxForm } from 'redux-form';
const submit = values => {
console.log('Form data:', values);
};
let SubmitForm = props => {
const { handleSubmit } = props;
return (
<form onSubmit={handleSubmit(submit)}>
<div>
<label htmlFor="firstName">First Name</label>
<Field name="firstName" component="input" type="text" />
</div>
<button type="submit">Submit</button>
</form>
);
};
SubmitForm = reduxForm({
form: 'submit'
})(SubmitForm);
export default SubmitForm;
Formik is a popular form library for React that provides a simpler API compared to redux-form. It focuses on reducing boilerplate and improving performance by not relying on Redux for form state management. Formik is often preferred for its ease of use and flexibility.
React Hook Form is a library that uses React hooks to manage form state and validation. It is known for its minimal re-renders and high performance. Unlike redux-form, it does not require Redux and is often chosen for its simplicity and performance benefits.
Final Form is a framework-agnostic form state management library that can be used with React through the react-final-form package. It offers similar functionality to redux-form but is designed to be more lightweight and flexible, without the need for Redux.
redux-form
works with React Redux to enable an html form in
React to use Redux to store all of its state.
props
from Form DataconnectReduxForm(formName, fields, validate?, touchOnBlur?, touchOnChange?)
connectReduxForm().async(asyncValidate, ...fields?)
reduxForm()
reduxForm().async()
props
- The props passed in to your form component by redux-form
npm install --save redux-form
This project follows SemVer and each release is posted on the Release Notes page.
Why would anyone want to do this, you ask? React a perfectly good way of keeping state in each component! The reasons are threefold.
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.
When used in conjunction with Redux Dev Tools, you can fast forward and rewind through your form data entry to better find bugs.
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.
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 // it is recommended that you use the key 'form'
}
const reducer = combineReducers(reducers);
const store = createStore(reducer);
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.
You will need to wrap your form component with redux-form
's connectReduxForm()
function.
IMPORTANT: If you are using
react-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 contactValidation from './contactValidation';
class ContactForm extends Component {
static propTypes = {
data: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired,
handleBlur: PropTypes.func.isRequired,
handleChange: PropTypes.func.isRequired,
handleSubmit: PropTypes.func.isRequired
}
render() {
const {
data: {name, address, phone},
errors, touched, handleBlur, handleChange, handleSubmit
} = this.props;
return (
<form onSubmit={handleSubmit}>
<label>Name</label>
<input type="text"
value={name}
onChange={handleChange('name')}
onBlur={handleBlur('name')}/>
{errors.name && touched.name ? <div>{errors.name}</div>}
<label>Address</label>
<input type="text"
value={address}
onChange={handleChange('address')}
onBlur={handleBlur('address')}/>
{errors.address && touched.address ? <div>{errors.address}</div>}
<label>Phone</label>
<input type="text"
value={phone}
onChange={handleChange('phone')}
onBlur={handleBlur('phone')}/>
{errors.phone && touched.phone ? <div>{errors.phone}</div>}
<button onClick={handleSubmit}>Submit</button>
</form>
);
}
}
// apply connectReduxForm() and include synchronous validation
ContactForm = connectReduxForm('contact', ['name', 'address', 'phone'], contactValidation)(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.
Using ES7 decorator proposal, the example above could be written as:
@connectReduxForm('contact', ['name', 'address', 'phone'], contactValidation)
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.
You may optionally supply a validation function, which is in the form ({}) => {}
and takes in all
your data and spits out error messages as well as a valid
flag. For example:
function contactValidation(data) {
const errors = { valid: true };
if(!data.name) {
errors.name = 'Required';
errors.valid = false;
}
if(data.address && data.address.length > 50) {
errors.address = 'Must be fewer than 50 characters';
errors.valid = false;
}
if(!data.phone) {
errors.phone = 'Required';
errors.valid = false;
} else if(!/\d{3}-\d{3}-\d{4}/.test(data.phone)) {
errors.phone = 'Phone must match the form "999-999-9999"'
errors.valid = false;
}
return errors;
}
You get the idea.
You must return a boolean valid
flag in the result.
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('contact', ['name', 'address', 'phone'], contactValidation)(ContactForm);
...changes to this:
function asyncValidation(data) {
return new Promise((resolve, reject) => {
const errors = {valid: true};
// do async validation
resolve(errors);
});
}
// apply connectReduxForm() and include synchronous AND asynchronous validation
ContactForm = connectReduxForm('contact', ['name', 'address', 'phone'], contactValidation)
.async(asyncValidation)(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('contact', ['name', 'address', 'phone'], contactValidation)
.async(asyncValidation, '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('contact', ['name', 'address', 'phone']).async(asyncValidation)(ContactForm);
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 'redux-form';
import {connect} from '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('contactForm', {})); // 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>
);
}
}
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 loginFormReducer = createFormReducer('loginForm', ['email', 'password']);
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,
data: {
...state.data,
password: '' // <----- clear password field
},
touched: {
...state.touched,
password: false // <------ also set password to 'untouched'
}
};
default:
return state;
}
}
})
}
const reducer = combineReducers(reducers);
const store = createStore(reducer);
Editing multiple records on the same page is trivially easy with redux-form
. All you have to do is to pass a
unique formKey
prop into your form element, and initialize the data with initializeWithKey()
instead of initialize()
. Let's say we want to edit many contacts on the same page.
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {initializeWithKey} from 'redux-form';
import {bindActionCreators} from 'redux';
import ContactForm from './ContactForm';
class ContactsPage extends Component {
static propTypes = {
contacts: PropTypes.array.isRequired,
initializeWithKey: PropTypes.func.isRequired
}
componentWillMount() {
const {contacts, initializeWithKey} = this.props;
contacts.forEach(function (contact) {
initializeWithKey('contact', String(contact.id), contact);
});
}
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
onSubmit={this.handleSubmit.bind(this, contact.id)}/>
})}
</div>
);
}
}
function mapStateToProps(state) {
return { contacts: state.contacts.data };
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ initializeWithKey }, dispatch),
}
// apply connect() to bind it to Redux state
ContactsPage = connect(mapStateToProps, mapDispatchToProps)(ContactsPage);
// export the wrapped component
export default ContactPage;
props
from Form DataYou 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.data.name
hasPhone: props => !!props.data.phone
})(ContactForm);
// THEN apply connectReduxForm() and include synchronous validation
ContactForm = connectReduxForm('contact', ['name', 'address', 'phone'], contactValidation)(ContactForm);
...
Or, in ES7 land...
@connectReduxForm('contact', ['name', 'address', 'phone'], contactValidation)
@mapProps({
hasName: props => !!props.data.name
hasPhone: props => !!props.data.phone
})
export default class ContactForm extends Component {
connect()
ing YourselfIf, 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 contactValidation from './contactValidation';
class ContactForm extends Component {
//...
}
// apply reduxForm() and include synchronous validation
ContactForm = reduxForm('contact', ['name', 'address', 'phone'], contactValidation)(ContactForm);
// ------- HERE'S THE IMPORTANT BIT -------
function mapStateToProps(state) {
return { form: state.placeWhereYouMountedFormReducer };
}
// apply connect() to bind it to Redux state
ContactForm = connect(mapStateToProps)(ContactForm);
// export the wrapped component
export default ContactForm;
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);
connectReduxForm(formName:string, fields:Array<string>, validate:Function?, touchOnBlur:boolean?, touchOnChange:boolean?)
formName : string
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
a list of all your fields in your form. This is used for marking all of the fields as
touched
on submit.
validate : Function
[optional]your synchronous validation function. Defaults to
() => ({valid: true})
touchOnBlur
: boolean [optional]marks fields to touched when the blur action is fired. Defaults to
true
touchOnChange
: boolean [optional]marks fields to touched when the change action is fired. Defaults to
false
connectReduxForm().async(asyncValidate:Function, ...fields:String?)
asyncValidate : Function
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.
...fields : String
[optional]field names for which
handleBlur
should trigger a call to theasyncValidate
function
reduxForm()
[NOT RECOMMENDED]
reduxForm()
has the same API asconnectReduxForm()
except that you must wrap the component inconnect()
yourself.
reduxForm().async()
[NOT RECOMMENDED]
reduxForm().async()
has the same API asconnectReduxForm().async()
except that you must wrap the component inconnect()
yourself.
The props passed into your decorated component will be:
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.
data : Object
The form data, in the form
{ field1: <string>, field2: <string> }
dirty : boolean
true
if the form data has changed from its initialized values. Opposite ofpristine
.
errors : Object
All the errors, in the form
{ field1: <string>, field2: <string> }
handleBlur(field:string) : Function
Returns a
handleBlur
function for the field passed.
handleChange(field:string) : Function
Returns a
handleChange
function for the field passed.
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))}>
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
.
isDirty : Function
a function that takes field name and returns whether or not that field is dirty (i.e. has a different value than the one passed in to
initialize
). Good for only showing errors when a field has changed (e.g.{errors.name && touched.name && isDirty('name') && <div>{errors.name}</div>}
). Opposite ofisPristine
.
isPristine : Function
a function that takes field name and returns whether or not that field is pristine (i.e. has the same value that was passed in with
initialize
). Opposite ofisDirty
.
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.
touched : Object
the touched flags for each field, in the form
{ field1: <boolean>, field2: <boolean> }
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
.
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
.
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
.
stopAsyncValidation(form:String, errors:Object)
Flips the
asyncValidating
flagfalse
and populatesasyncErrors
.
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.
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.
FAQs
A higher order component decorator for forms using Redux and React
The npm package redux-form receives a total of 220,976 weekly downloads. As such, redux-form popularity was classified as popular.
We found that redux-form demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.