React~Redux Form - rohit120582sharma/Documentation GitHub Wiki
-
A versatile, fast, and intuitive library for creating complex and performant forms in React and Redux
-
RRF itself persist the state of the form into a model in the redux store. In other words, RRF stores form's data in a model in the redux store.
-
RRF provides a collection of reducer creators and action creators that enables us to do all this work without writing much code.
-
RRF supports for form validation
-
If you want to use React-Redux-Form purely for its form validation and not require the form data to be persistent in the redux store, instead if you want it to be persistent in the local state of the component, then you can simply use a
LocalForm
that is also supported by React-Redux-Form to create forms.- Maps form model to local state of the component
- Suitable when you don't need form data persistence across component mouting/unmouting
- Can still perform form validation using support from React-Redux-Form
- Implement
onSubmit
as a arrow function
- https://davidkpiano.github.io/react-redux-form/docs.html
- https://github.com/davidkpiano/react-redux-form/blob/master/SUMMARY.md
- https://github.com/davidkpiano/react-redux-form/blob/master/docs/guides/react-native.md
- https://github.com/davidkpiano/react-redux-form/blob/master/src/native.js
- https://swizec.com/blog/populate-react-redux-form-dynamic-default-values/swizec/8158
npm install react --save
npm install react-dom --save
npm install redux --save
npm install react-redux --save
npm install react-redux-form --save
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
// We'll create this in next step
import store from './store.js';
// We'll create this in next step
import MyForm from './components/MyForm.js';
class App extends React.Component {
render() {
return (
<Provider store={ store }>
<MyForm />
</Provider>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
import { createStore, combineReducers } from 'redux';
import { combineForms } from 'react-redux-form';
const initialUser = {
firstName: '',
lastName: '',
};
export const store = createStore(combineReducers({
forms: combineForms({
user: initialUser,
}, 'forms')
}));
import React from 'react';
import { Form, Control, Errors } from 'react-redux-form';
class MyForm extends React.Component {
handleSubmit(values) {
// Do anything you want with the form value
console.log(values);
}
render() {
return (
<Form model="user" onSubmit={(val) => this.handleSubmit(val)}>
<label>Your name?</label>
<Control.text
model="user.name"
validators={{
required: (val) => !!val.length,
minLength: (val) => val.length > 8,
}}
/>
<Errors
model="user.username"
messages={{
required: 'Username is required.',
minLength: 'Username must be more than 8 characters.',
}}
/>
<label>
<Control.checkbox model="user.remember" />
Remember me
</label>
<button>Submit!</button>
</Form>
);
}
}
// No need to connect()!
export default MyForm;
A model represents the data that the user can interact with.
To set up your app's models for RRF, it's recommended to use combineForms()
, which takes in the initial state (or custom reducers) of all your models.
This works similar to combineReducers()
in redux, and will create a single reducer()
that converts each key/value pair in the object given to combineForms()
into a modelReducer()
, and also set up a single formReducer()
under the 'forms'
key.
const store = createStore(combineReducers({
foo: fooReducer,
bar: barReducer,
forms: combineForms({
user: initialUser,
goat: initialGoat,
}, 'forms'), // <== specify the deep model path here
}));
Similar to combineReducers()
in redux, the combineForms()
helper function takes a forms
object where:
- each key is a string model path
- each value is either:
- (Any) an initial state, or
- (Function) an existing reducer
and turns it into a single reducing function:
- each key/value pair is a
modelReducer()
- a
'forms'
key on the same object is a singleformReducer()
that handles all forms for all models.
Validation occurs as the result of dispatching validation actions, such as actions.setValidity(model, validity)
or actions.setErrors(model, errors)
. That action updates the form validity and error state of your model, and allows you to:
- validate any part of the model state (however deep)
- validate any key on that model (such as
{ required: true, length: false }
) - call validation only when the model has been updated.
The <Control>
components accept a few validation-specific props:
-
validators
- an object with key-value pairs:- validation key (string) and
- validator (function) - a function that takes in the model
value
and returns a boolean (true/false if valid/invalid)
-
asyncValidators
- an object with key-value pairs:- validation key (string) and
- validator (function) - a function that takes in the model
value
and thedone
callback, similar toasyncSetValidity(value, done)
-
validateOn
andasyncValidateOn
- event to indicate when to validate:-
"change"
(default for validators) -
"blur"
(default for asyncValidators) "focus"
- or multiple values, such as
["change", "blur"]
-
Define a few functions that can help us to do validation.
const required = (val) => val && val.length;
const maxLength = (len) => (val) => !(val) || (val.length <= len);
const minLength = (len) => (val) => val && (val.length >= len);
const isNumber = (val) => !isNaN(Number(val));
const validEmail = (val) => /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(val);
<Control.text
model="user.firstName"
validators={{
required: required,
}}
validateOn="change" />
Any validation across models is best represented as a form-level validator. For instance, say you have a form where the two password fields, .password
and .confirmPassword
, need to match:
[Note] You can manually retrieve form-level validation by accessing
[form path].$form.validity
, which represents the validity of the entire form.
<Form
model="user"
validators={{
'passwordsMatch': (vals) => vals.password === vals.confirmPassword,
}}>
<Control type="password" model=".password" />
<Control type="password" model=".confirmPassword" />
<Errors model="user" />
</Form>
You can have deep validators in the <Form validators={{...}}>
prop. The empty brackets in the validator key 'phones[].number'
tell RRF to validate the .number property for each phone
in the user.phones[]
array.
// Suppose you have a store with this 'user' model:
// {
// name: 'Bob',
// phones: [
// { type: 'home', number: '5551231234' },
// { type: 'cell', number: '5550980987' },
// ],
// }
<Form
model="user"
validators={{
'phones[].number': (value) => value && value.length === 10,
}}>
{/* etc. */}
</Form>
import React, { Component } from 'react';
import { Button, Row, Col, Label } from 'reactstrap';
import { Control, LocalForm, Errors } from 'react-redux-form';
class ContactForm extends Component {
handleSubmit(values) {
console.log('Current State is: ' + JSON.stringify(values));
alert('Current State is: ' + JSON.stringify(values));
}
render(){
return (
<LocalForm onSubmit={(values) => this.handleSubmit(values)}>
<Row className="form-group">
<Label htmlFor="firstname" md={2}>First Name</Label>
<Col md={10}>
<Control.text
model=".firstname"
id="firstname"
name="firstname"
placeholder="First Name"
className="form-control"
validators={{
required: (val) => val && val.length,
}} />
<Errors
model=".firstname"
show="touched"
className="text-danger"
messages={{
required: 'Required'
}} />
</Col>
</Row>
<Row className="form-group">
<Label htmlFor="lastname" md={2}>Last Name</Label>
<Col md={10}>
<Control.text
model=".lastname"
id="lastname"
name="lastname"
placeholder="Last Name"
className="form-control"
validators={{
required: (val) => val && val.length,
}} />
<Errors
model=".lastname"
show="touched"
className="text-danger"
messages={{
required: 'Required'
}} />
</Col>
</Row>
<Row className="form-group">
<Label htmlFor="email" md={2}>Email</Label>
<Col md={10}>
<Control.text
model=".email"
id="email"
name="email"
placeholder="Email"
className="form-control"
validators={{
required: (val) => val && val.length,
validEmail: (val) => /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(val)
}} />
<Errors
model=".email"
show="touched"
className="text-danger"
messages={{
required: 'Required',
validEmail: 'Invalid Email Address',
}} />
</Col>
</Row>
<Row className="form-group">
<Col md={{size: 6, offset: 2}}>
<div className="form-check">
<Label check>
<Control.checkbox
model=".agree"
name="agree"
className="form-check-input" />
{' '} <strong>May we contact you?</strong>
</Label>
</div>
</Col>
<Col md={{size: 3, offset: 1}}>
<Control.select
model=".contactType"
name="contactType"
className="form-control"
validators={{
required: (val) => val && val.length
}}>
<option>Tel.</option>
<option>Email</option>
</Control.select>
<Errors
model=".contactType"
show="touched"
className="text-danger"
messages={{
required: 'Required'
}} />
</Col>
</Row>
<Row className="form-group">
<Col md={{size:10, offset: 2}}>
<Button type="submit" color="primary">
Send Feedback
</Button>
</Col>
</Row>
</LocalForm>
);
}
}
export default ContactForm;