<img alt="gzip size" src="https://img.shields.io/badge/gzip%20size-7%20kB-brightgreen.svg" /> <img alt="PRs welcome" src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" />
It's a library inspired by the Angular's Reactive Forms, which allows to create a tree of form control objects in the component class and bind them with native form control elements.
FormGenerator
api to create large forms with less code.FormGroup
& FormArray
apis.npm install react-reactive-form --save
import React, { Component } from 'react';
import {
FormBuilder,
FieldGroup,
FieldControl,
Validators,
} from "react-reactive-form";
const TextInput = ({ handler, touched, hasError, meta }) => (
<div>
<input placeholder={`Enter ${meta.label}`} {...handler()}/>
<span>
{touched
&& hasError("required")
&& `${meta.label} is required`}
</span>
</div>
)
export default class Login extends Component {
loginForm = FormBuilder.group({
username: ["", Validators.required],
password: ["", Validators.required],
rememberMe: false
});
handleReset=() => {
this.loginForm.reset();
}
handleSubmit=(e) => {
e.preventDefault();
console.log("Form values", this.loginForm.value);
}
render() {
return (
<FieldGroup
control={this.loginForm}
render={({ get, invalid }) => (
<form onSubmit={this.handleSubmit}>
<FieldControl
name="username"
render={TextInput}
meta={{ label: "Username" }}
/>
<FieldControl
name="password"
render={TextInput}
meta={{ label: "Password" }}
/>
<FieldControl
name="rememberMe"
render={({handler}) => (
<div>
<input {...handler("checkbox")}/>
</div>
)}
/>
<button
type="button"
onClick={this.handleReset}
>
Reset
</button>
<button
type="submit"
disabled={invalid}
>
Submit
</button>
</form>
)}
/>
);
}
}
import React, { Component } from 'react';
import {
Validators,
FormGenerator
} from "react-reactive-form";
// Input component
const TextInput = ({ handler, touched, hasError, meta }) => (
<div>
<input placeholder={`Enter ${meta.label}`} {...handler()}/>
<span>
{touched
&& hasError("required")
&& `${meta.label} is required`}
</span>
</div>
)
// Checkbox component
const CheckBox = ({ handler }) => (
<div>
<input {...handler("checkbox")}/>
</div>
)
// Field config to configure form
const fieldConfig = {
controls: {
username: {
options: {
validators: Validators.required
},
render: TextInput,
meta: { label: "Username" }
},
password: {
options: {
validators: Validators.required
},
render: TextInput,
meta: { label: "Password" }
},
rememberMe: {
render: CheckBox
},
$field_0: {
isStatic: false,
render: ({ invalid, meta: { handleReset } }) => (
<div>
<button
type="button"
onClick={handleReset}
>
Reset
</button>
<button
type="submit"
disabled={invalid}
>
Submit
</button>
</div>
)
}
},
}
export default class Login extends Component {
handleReset=() => {
this.loginForm.reset();
}
handleSubmit=(e) => {
e.preventDefault();
console.log("Form values", this.loginForm.value);
}
setForm = (form) => {
this.loginForm = form;
this.loginForm.meta = {
handleReset: this.handleReset
}
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<FormGenerator
onMount={this.setForm}
fieldConfig={fieldConfig}
/>
</form>
);
}
}
You can also create controls without even initializing the group control object with the help of new react form components ( FieldGroup, FieldControl, FieldArray).
import React, { Component } from 'react'
import { FieldGroup, FieldControl, Validators } from 'react-reactive-form'
export default class Login extends Component {
handleSubmit = (e, value) => {
console.log('Form values', value)
e.preventDefault()
}
render() {
return (
<FieldGroup
render={({ get, invalid, reset, value }) => (
<form onSubmit={e => this.handleSubmit(e, value)}>
<FieldControl
name="username"
options={{ validators: Validators.required }}
render={({ handler, touched, hasError }) => (
<div>
<input {...handler()} />
<span>
{touched && hasError('required') && 'Username is required'}
</span>
</div>
)}
/>
<FieldControl
name="password"
options={{ validators: Validators.required }}
render={({ handler, touched, hasError }) => (
<div>
<input {...handler()} />
<span>
{touched && hasError('required') && 'Password is required'}
</span>
</div>
)}
/>
<FieldControl
name="rememberMe"
render={({ handler }) => (
<div>
<input {...handler('checkbox')} />
</div>
)}
/>
<button type="button" onClick={() => reset()}>
Reset
</button>
<button type="submit" disabled={invalid}>
Submit
</button>
</form>
)}
/>
)
}
}
So, it's not mandatory that you need to define your control separately but if you want a better control over your form state then you should do that, if your controls are dynamic then you can also initalize the empty group control and add the controls later. See the example:
import React, { Component } from 'react'
import {
FormBuilder,
FieldGroup,
FieldControl,
Validators
} from 'react-reactive-form'
export default class Login extends Component {
// Initialize the empty group control
loginForm = FormBuilder.group({})
handleReset = e => {
this.loginForm.reset()
}
handleSubmit = e => {
console.log('Form values', this.loginForm.value)
e.preventDefault()
}
render() {
return (
<FieldGroup
control={this.loginForm}
render={({ get, invalid, reset, value }) => (
<form onSubmit={this.handleSubmit}>
<FieldControl
name="username"
options={{ validators: Validators.required }}
render={({ handler, touched, hasError }) => (
<div>
<input {...handler()} />
<span>
{touched && hasError('required') && 'Username is required'}
</span>
</div>
)}
/>
<FieldControl
name="password"
options={{ validators: Validators.required }}
render={({ handler, touched, hasError }) => (
<div>
<input {...handler()} />
<span>
{touched && hasError('required') && 'Password is required'}
</span>
</div>
)}
/>
<FieldControl
name="rememberMe"
render={({ handler }) => (
<div>
<input {...handler('checkbox')} />
</div>
)}
/>
<button type="button" onClick={this.handleReset}>
Reset
</button>
<button type="submit" disabled={invalid}>
Submit
</button>
</form>
)}
/>
)
}
}
Try out react-reactive-forms
in these sandbox versions of the Examples.
React has many libraries which works on the form logic, but here are some concerns with these:
If you’re using the redux-form then you should know the pain, for just a two field login form you’d to write the store logic.In RRF you can see that how simple is to deal with simple and complex forms.
And one of the awesome thing is that you can just write your form controls logic anywhere in your application.
Many libraries come with dependencies for e.g redux is required for redux-form, So what If I’m using another state management or not event using any.
According to Dan Abramov, form state is inherently ephemeral and local, so tracking it in Redux (or any kind of Flux library) is unnecessary.
RRF comes with zero
dependency, So it’s totally up to you that how you want to save your form state if needed.
Now that’s a big problem with almost all libraries when you're dealing with large forms.
How RRF does solve performance issues ?
updateOn
property.With the help of subscribers it's pretty easy to listen for a particular state changes and modify the controls accordingly.
value
and status
changes subscribers?RRF uses inbuilt Subject
, A Subject
is an object with the method next(v).To feed a new value to the Subject,RRF just calls the next(theValue), and it will be multicasted to the Observers registered to listen to the Subject.
So basically it provides three subjects for each AbstractControl valueChanges
, statusChanges
and stateChanges
and additional two subjects for FormControl ( onValueChanges
, onBlurChanges
)
You can register an observer to a particular Subject to do some actions whenever some particular changes happen.
Example:
componentDidMount() {
this.myForm.get(“gender”).valueChanges.subscribe((value) => {
// do something
})
}
Checkout the Basic usage guide for more details.
Field components are subscribed to the state changes of a particular control which means that it’ll re-render the component only when it’s state changes disregarding of other field changes.You can also implement your custom wrappers by using the stateChanges Subject
.
Its an another performance booster in RRF, it just holds the computation needed to be made after every keystroke or value changes until you want to execute.It has three options change
(default), blur
and submit
, you can define all of them at both field and record level.
Yes, this library works with react-native also, currently it supports react-native TextInput
and Switch
component.
If you're using react-native then please add the following line of code in index.js
of your project to avoid error in android devices.
import "core-js/es6/symbol";
import "core-js/fn/symbol/iterator";
Let's make React Reactive Forms better! If you're interested in helping, all contributions are welcome and appreciated.
And don't forget to star the repo, I will ensure more frequent updates! Thanks!
This project exists thanks to all the people who contribute.
Thank you to all our backers! 🙏 [Become a backer]
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]