React~Redux Form - rohit120582sharma/Documentation GitHub Wiki

Introduction

  • 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

References



Getting start

Install the dependencies

npm install react --save
npm install react-dom --save
npm install redux --save
npm install react-redux --save
npm install react-redux-form --save

Setup your app

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

Setup your store

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

Setup your form with Validators & Errors

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;


Models

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

combineForms API

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 single formReducer() that handles all forms for all models.


Validation

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.

Validation with <Control> components

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 the done callback, similar to asyncSetValidity(value, done)
  • validateOn and asyncValidateOn - event to indicate when to validate:
    • "change" (default for validators)
    • "blur" (default for asyncValidators)
    • "focus"
    • or multiple values, such as ["change", "blur"]

Example

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" />

Validating across models

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>


Example

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;
⚠️ **GitHub.com Fallback** ⚠️