Angular like reactive forms in React
React Reactive Forms
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.
Features
- UI independent.
- Zero dependencies.
- Nested forms.
- Subscribers for value & status changes of controls.
- Provides a set of validators & also supports custom sync & async validators.
- Better form management with
FormGroup
&FormArray
apis. - Customizable update strategy for better performace with large forms.
Installation
npm install react-reactive-form --save
Basic Example
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>
)}
/>
);
}
}
Add Controls Dynamically
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>
)}
/>
)
}
}