redux-form-manager
Version:
Redux form manager
907 lines (805 loc) • 24.2 kB
Markdown
# Redux Form Manager
A Form control built with and for React with Redux. Initially built for use in Redux Project
> Validatation in form is easy to go...
### If you liked, gimme a star, Thanks.
## Release Issues
### Latest version is `v3.0.0`
* `v3.0.0`
- Update feature core.getFormError to dispatch firstError of formData to reducer.
- Update feature export new function { getErrorMessage } from 'redux-form-manager'
- Update New Demo <https://hlex.github.io/redux-form-manager/demo>
* `v2.16.0`
- Fixed bugs rules `equalLength`, `maxLength`, `minLength` cannot validate and show message properly.
- Update rules usage in README.md
* `v2.15.0`
- Add `lodash` to peerDependencies for features updateNestedFieldData
* `v2.14.0`
- Add feature - add props `afterUpdateWhenValid` (default = false) (thanks for [KIRAN H](https://github.com/KIRAN-H))
* `v2.13.0`
- Fixed bugs Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the FormValidation component. (thanks for [KIRAN H](https://github.com/KIRAN-H))
* `v2.12.0`
- When `errorMessage` is not empty, afterFieldChange won't trigger. (thanks for [KIRAN H](https://github.com/KIRAN-H))
- `errorMessage` is assigned to '' (empty String) when value is valid. (#issue2 closed thanks for [GA-MO](https://github.com/GA-MO))
* `v2.10.0`
- Update React to version 15.6.0
- Using PropTypes from 'prop-types'
* `v2.9.0`
- Add feature - `props in formData` formData: (state, props) => {}
## Special Thanks to 'GA-MO'
[![alt text][logo]](https://github.com/GA-MO)
[logo]: https://avatars2.githubusercontent.com/u/18608702?v=4&s=400 "GA-MO"
### This Component Repository created by [*React Component Boilerplate*](<https://github.com/GA-MO/react-component-npm-package-boilerplate/>)
React Component boilerplate for creating new React Compoment and everything you need to get started.
I recommend you to use it if you wanna create some awesome component for this world !
## Prerequisite
This project uses library React-Redux-Gamo-boilerplate, React, Redux, React-Redux and ES2015 syntax, so make sure that you are capable with it.
- React-Redux-Gamo-boilerplate https://github.com/GA-MO/react-redux-gamo-boilerplate
- ES2015 https://babeljs.io/learn-es2015/
- React https://facebook.github.io/react/
- Redux http://redux.js.org/
- React-Redux https://github.com/reactjs/react-redux
## Fast date with Demo & Examples
Live demo: <https://hlex.github.io/redux-form-manager/demo>
The live demo is still running redux-form-manager `v3.0.0`
### Welcome to clone or fork and give me some pull request.
you can run this repository in your local machine by this command
```
$ npm run dev
```
it will serve at `localhost:9000`
## Getting Started
```
$ npm install redux-form-manager --save
```
or if you prefer yarn..
```
$ yarn add redux-form-manager
```
## WARNING !
### Basically, we are using loganfsmyth/babel-plugin-transform-decorators-legacy (@ symbol) in any tutorials so make sure that you have added it in your .babelrc
Read more about [loganfsmyth/babel-plugin-transform-decorators-legacy](https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy)
### if you don't want to, let's using this format to activate instead.
### Stand Alone
```js
export default bindFormValidation(core, afterFieldChange, mapStateToValidationPriority)(YourContainer)
```
### With Connect (React-Redux)
```js
const connectedComponent = connect(mapStateToProps, mapDispatchToProps)(YourContainer)
export default bindFormValidation(core, afterFieldChange, mapStateToValidationPriority)(
connectedComponent
)
```
## Advanced Usage: Reveal Key Features
There are many features we have provided
- [Rendering User Interface with any React Components.](#feature-custom-ui)
- [Doing things after update function when any fields dispatched.](#feature-after-update)
- [Creating custom validation rules.](#feature-custom-validation)
- [Handling dynamic field properties.](#feature-dynamic-field-props)
- [Customizing action type of each field.](#feature-customize-action)
- [Validating each field by priority.](#feature-validation-priority)
- [Using Nested field data such as array of object.](#feature-nested-field)
- [Dispatch firstError to outside container.](#feature-dispatch-firsterror)
- [Using other event to dispatch.](#feature-other-dispatch)
## Basic Usage: Just copy paste
Create new js file and then copy following codes
```js
import React, { Component } from 'react'
import { bindFormValidation } from 'redux-form-manager'
class TextInputField extends Component {
handleChange = (e) => {
const { onChange } = this.props
onChange(e.target.value)
}
render = () => {
const {
label,
value,
disable,
placeholder,
hidden,
errorMessage,
} = this.props
return (
<div className={`box-form-input ${hidden && 'hidden'}`}>
<label>{label}</label>
<input
type='text',
value={value}
disabled={disabled}
placeholder={placeholder}
onChange={this.handleChange}
/>
<div className='error-message'>{errorMessage}</div>
</div>
)
}
}
const createForm = (state) => {
return {
firstname: {
name: 'firstname',
type: 'input',
label: 'Firstname',
value: '',
placeholder: 'write down your firstname',
disabled: false,
hidden: false,
rules: {
required: 'Please fill in your firstname.'
},
},
lastname: {
name: 'lastname',
type: 'input',
label: 'Lastname',
value: '',
placeholder: 'write down your lastname',
disabled: false,
hidden: false,
rules: {
required: 'Please fill in your lastname.'
},
}
}
}
const core = {
actionType: 'FORM/CHANGE/CUSTOMER',
formData: (state, props) => createForm(state),
renderUIInputField: (fieldData, updateValue) => {
return <TextInputField {...fieldData} onChange={updateValue} />
}
}
@bindFormValidation(core)
export default class Form extends Component {
render() {
const { formData, renderInputField, firstError } = this.props
return (
<div>
<h1>Form</h1>
{renderInputField(formData.firstname)}
{renderInputField(formData.lastname)}
<h4>error: {firstError}</h4>
</div>
)
}
}
```
Boom! you got a form with field that could be validated, your page also.
Look at your console that
# Documentation
## Table of Contents
- ### Get Started
- [Concept](#concept)
- [Setup](#setup)
- [Core](#core)
- [Working Example](#working-example)
- ### Form Data & Field Data Properties
- [Form Data](#form-data)
- [Field Data](#field-data)
- ### Features
- [Rendering User Interface with any React Components.](#feature-switch-ui)
- [Customizing RenderInputField function.](#feature-custom-ui)
- [Doing things after update function when any fields dispatched.](#feature-after-update)
- [Creating custom validation rules.](#feature-custom-validation)
- [Handling dynamic field properties.](#feature-dynamic-field-props)
- [Customizing action type of each field.](#feature-customize-action)
- [Validating each field by priority.](#feature-validation-priority)
- [Using Nested field data such as array of object.](#feature-nested-field)
- [Dispatch firstError to outside container.](#feature-dispatch-firsterror)
- [Using other event to dispatch.](#feature-other-dispatch)
## <a id="concept"></a>Concept
Coming soon.
## <a id="setup"></a>Setup
install npm packages
```
$ npm install redux-form-manager --save
```
or if you prefer yarn..
```
$ yarn add redux-form-manager --save
```
import library at the start of your js file
```js
import { bindFormValidation } from 'redux-form-manager'
```
### InputField
InputField is a UI component that receive event from user. You can create any JSX UI as you want. You just reminded that binding function props with renderUIInputField updateValue.
```js
import React, { Component } from 'react'
class TextInputField extends Component {
handleChange = (e) => {
const { onUpdateValue } = this.props
onUpdateValue(e.target.value)
}
render = () => {
const {
label,
value,
disable,
placeholder,
hidden,
errorMessage,
} = this.props
return (
<div className={`box-form-input ${hidden && 'hidden'}`}>
<label>{label}</label>
<input
type='text',
value={value}
disabled={disabled}
placeholder={placeholder}
onChange={this.handleChange}
/>
<div className='error-message'>{errorMessage}</div>
</div>
)
}
}
```
### Tips for InputField
You can create InputField Component that can morph to other inputs by defining props 'type' such as text, select, checkbox, radio etc.
```js
import React, { Component } from 'react'
import TextInput from 'your/component/path'
import SelectInput from 'your/component/path'
class InputField extends Component {
render() {
switch (type) {
case 'text':
return <TextInput {...this.props}></TextInput>
case 'select':
return <SelectInput {...this.props}></SelectInput>
default:
return <TextInput {...this.props}></TextInput>
}
}
}
export default InputField
```
### bindFormValidation()
bindFormValidation is a higher order function that return function which grant component to have form manager modules which are 'formData', 'renderInputField()', 'firstError'
## <a id="core"></a>Core
core is an object to assign variables to bindFormValidation()
### all of these keys are required. (Please copy them)
```js
const core = {
actionType: 'FORM/CHANGE/CUSTOMER',
formData: (state, props) => createForm(state),
renderUIInputField: (fieldData, updateValue) => {
return <InputField {...fieldData} onChange={updateValue} />
}
}
@bindFormValidation(core)
```
### actionType (defaultActionType in action)
As you know, redux's store has dispatch function which it is a process that fire action to reducer contains actionType and any variables.
defaultActionType is a default actionType of store.dispatch when you didn't send fieldData's actionType (we'll talk about this later).
## <a id="working-example"></a>Working Example
Create new js file and then copy following codes
```js
import React, { Component } from 'react'
import InputField, { bindFormValidation } from 'redux-form-manager'
const createForm = (state) => {
return {
firstname: {
name: 'firstname',
type: 'input',
label: 'Firstname',
value: '',
placeholder: 'write down your firstname',
disabled: false,
hidden: false,
rules: [
required: 'Please fill in your firstname.'
],
},
lastname: {
name: 'lastname',
type: 'input',
label: 'Lastname',
value: '',
placeholder: 'write down your lastname',
disabled: false,
hidden: false,
rules: [
required: 'Please fill in your lastname.'
],
}
}
}
const core = {
actionType: 'FORM/CHANGE/CUSTOMER',
formData: (state, props) => createForm(state),
renderUIInputField: (fieldData, updateValue) => {
switch (fieldData.type)
return <InputField {...fieldData} onChange={updateValue} />
}
}
@bindFormValidation(core)
export default class Form extends Component {
render() {
const { formData, renderInputField, firstError } = this.props
return (
<div>
<h1>Form</h1>
{
renderInputField(formData.firstname)
}
{
renderInputField(formData.lastname)
}
<h4>error: {firstError}</h4>
</div>
)
}
}
```
## <a id="form-data"></a>Form Data
### The common variable of this package is 'formData'
formData is a metadata of the form which it's type is Object. you can describe properties of your form in this variable. In any formData, it contains many fieldDatas.
### Example
```
const formData = {
firstname: {
name: 'firstname',
type: 'input',
label: 'Firstname',
value: '',
placeholder: 'write down your firstname',
disabled: false,
hidden: false,
rules: [
required: 'Please fill in your firstname.'
],
},
}
```
## <a id="field-data"></a>Field Data
### Another variable of this package, 'fieldData'
fieldData is an Object that define properties of the field. For example, firstname field would have properties like this
### Field Data Schema (Recommended)
| Property | Type
| ------------- |:-------------:
| name | String (*)
| value | String, Object (*)
| rules | Object (*)
| type | String
| label | String
| placeholder | String
| disabled | Boolean
| hidden | Boolean
(*) is required
### You can add any keys as your Component need.
we can define fieldData as follow.
```js
const firstname = {
name: 'firstname',
type: 'input',
label: 'Firstname',
value: '',
placeholder: 'write down your firstname',
disabled: false,
hidden: false,
rules: {
required: 'Please fill in your firstname.'
},
},
```
## <a id="feature-switch-ui"></a>Switch UI
In ordinary form, we know that there are many input types such as 'text', 'select', 'radio', 'checkbox' etc. In redux-form-manager we also provide you to config your input type.
### just switch (case) in renderUIInputField
```js
import React, { Component } from 'react'
import { bindFormValidation } from 'redux-form-manager'
import TextInput from 'your/component/path'
import SelectInput from 'your/component/path'
const createForm = (state) => {
return {
firstname: {
name: 'firstname',
type: 'text',
label: 'Firstname',
value: '',
placeholder: 'write down your firstname',
disabled: false,
hidden: false,
rules: {
required: 'Please fill in your firstname.'
},
},
age: {
name: 'age',
type: 'select',
label: 'Age',
value: '',
placeholder: '',
disabled: false,
hidden: false,
options: [
{
label: '15 years old',
value: '15',
},
{
label: '20 years old',
value: '20',
}
]
rules: {
required: 'Please select your age.'
},
}
}
}
const core = {
actionType: 'FORM/CHANGE/CUSTOMER',
formData: (state, props) => createForm(state),
renderUIInputField: (fieldData, updateValue) => {
switch (fieldData.type) {
case 'text':
return <TextInput {...fieldData} onChange={updateValue} />
case 'select':
return <SelectInput {...fieldData} onChange={updateValue} />
default:
return <InputField {...fieldData} onChange={updateValue} />
}
}
}
@bindFormValidation(core)
export default class Form extends Component {
render() {
const { formData, renderInputField, firstError } = this.props
return (
<div>
<h1>Form</h1>
{ renderInputField(formData.firstname) }
{ renderInputField(formData.age) }
<h4>error: {firstError}</h4>
</div>
)
}
}
```
## <a id="feature-custom-ui"></a>Custom UI
Building your own custom UI, we are going to use 2nd parameter of renderInputField.
```js
import React, { Component } from 'react'
import { bindFormValidation } from 'redux-form-manager'
const createForm = (state) => {
return {
firstname: {
name: 'firstname',
type: 'input',
label: 'Firstname',
value: '',
placeholder: 'write down your firstname',
disabled: false,
hidden: false,
rules: {
required: 'Please fill in your firstname.'
},
},
lastname: {
name: 'lastname',
type: 'input',
label: 'Lastname',
value: '',
placeholder: 'write down your lastname',
disabled: false,
hidden: false,
rules: {
required: 'Please fill in your lastname.'
},
}
}
}
const core = {
actionType: 'FORM/CHANGE/CUSTOMER',
formData: (state, props) => createForm(state),
renderUIInputField: (fieldData, updateValue) => {
return <InputField {...fieldData} onChange={updateValue} />
}
}
@bindFormValidation(core)
export default class Form extends Component {
renderMyInputComponent = (fieldData, updateValue) => {
return (
<MyInputComponent
{...fieldData}
onUpdateValue={(value) => updateValue(fieldData.key, value)}
/>
)
}
render() {
const { formData, renderInputField, firstError } = this.props
return (
<div>
<h1>Form</h1>
{ renderInputField(formData.firstname, this.renderMyInputComponent) }
{ renderInputField(formData.lastname) }
<h4>error: {firstError}</h4>
</div>
)
}
}
class MyInputComponent extends Component {
handleChange = (e) => {
const { onUpdateValue } = this.props
onUpdateValue(e.target.value)
}
render() {
return (
const {
label,
value,
disable,
placeholder,
hidden,
errorMessage,
} = this.props
<div className={`box-form-input ${hidden && 'hidden'}`}>
<label>{label}</label>
<input
type='text',
value={value}
disabled={disabled}
placeholder={placeholder}
onChange={this.handleChange}
/>
<div className='error-message'>{errorMessage}</div>
</div>
)
}
}
```
## <a id="feature-validation-priority"></a>mapStateToValidationPriority
Validating each field by priority.
```js
import React, { Component } from 'react'
import InputField, { bindFormValidation } from 'redux-form-manager'
const createForm = (state) => {
return {
firstname: {
name: 'firstname',
type: 'input',
label: 'Firstname',
value: '',
placeholder: 'write down your firstname',
disabled: false,
hidden: false,
rules: {
required: 'Please fill in your firstname.'
},
},
lastname: {
name: 'lastname',
type: 'input',
label: 'Lastname',
value: '',
placeholder: 'write down your lastname',
disabled: false,
hidden: false,
rules: {
required: 'Please fill in your lastname.'
},
}
}
}
const mapStateToValidationPriority = state => {
return ['lastname', 'firstname']
}
const core = {
actionType: 'FORM/CHANGE/CUSTOMER',
formData: (state, props) => createForm(state),
renderUIInputField: (fieldData, updateValue) => {
return <InputField {...fieldData} onChange={updateValue} />
}
}
@bindFormValidation(core, null, mapStateToValidationPriority)
```
## <a id="feature-after-update"></a>afterFieldChange
Doing after update function when any field dispatch.
```js
import React, { Component } from 'react'
import InputField, { bindFormValidation } from 'redux-form-manager'
const createForm = (state) => {
return {
firstname: {
name: 'firstname',
type: 'input',
label: 'Firstname',
value: '',
placeholder: 'write down your firstname',
disabled: false,
hidden: false,
rules: {
required: 'Please fill in your firstname.'
},
},
lastname: {
name: 'lastname',
type: 'input',
label: 'Lastname',
value: '',
placeholder: 'write down your lastname',
disabled: false,
hidden: false,
rules: {
required: 'Please fill in your lastname.'
},
}
}
}
const mapStateToValidationPriority = state => {
return []
}
const afterFieldChange = (dispatch, state) => {
return {
firstname: (value, key) => {
dispatch({
type: 'FORM/CHANGE/CUSTOMER',
key: 'lastname',
value: value
})
},
}
}
const core = {
actionType: 'FORM/CHANGE/CUSTOMER',
formData: (state, props) => createForm(state),
renderUIInputField: (fieldData, updateValue) => {
return <InputField {...fieldData} onChange={updateValue} />
}
}
@bindFormValidation(core, afterFieldChange, mapStateToValidationPriority)
```
## <a id="feature-customize-action"></a>actionType
Customizing action type of each field.
```js
import React, { Component } from 'react'
import InputField, { bindFormValidation } from 'redux-form-manager'
const createForm = (state) => {
return {
firstname: {
actionType: 'MY_ACTION_FIRSTNAME',
name: 'firstname',
type: 'input',
label: 'Firstname',
value: '',
placeholder: 'write down your firstname',
disabled: false,
hidden: false,
rules: {
required: 'Please fill in your firstname.'
},
},
lastname: {
actionType: 'MY_ACTION_LASTNAME',
name: 'lastname',
type: 'input',
label: 'Lastname',
value: '',
placeholder: 'write down your lastname',
disabled: false,
hidden: false,
rules: {
required: 'Please fill in your lastname.'
},
}
}
}
const afterFieldChange = (dispatch, state) => {
return {
firstname: (value, key) => {
dispatch({
type: 'FORM/CHANGE/CUSTOMER',
key: 'lastname',
value: value
})
},
}
}
const core = {
actionType: 'FORM/CHANGE/CUSTOMER',
formData: (state, props) => createForm(state),
renderUIInputField: (fieldData, updateValue) => {
return <InputField {...fieldData} onChange={updateValue} />
}
}
@bindFormValidation(core, afterFieldChange)
```
## <a id="feature-custom-validation"></a>Custom Validation Rules
### Rules build-in list
```js
rules: {
'required': `${message}`,
'require': `${message}`,
'email': `${message}`,
'thaiMobile': `${message}`,
'thaiPhone': `${message}`,
'thaiId': `${message}`,
'thaiFullname': `${message}`,
'equalLength': {
length: `${length}`,
message: `${message}`
},
'maxLength': {
maxLength: `${length}`,
message: `${message}`
},
'minLength': {
minLength: `${length}`,
message: `${message}`
},
'alphabet': `${message}`,
'number': `${message}`,
'correctBracket': `${message}`,
'notStartWithSpacing': `${message}`,
'notContainDoubleSpacing': `${message}`,
'notEndWithSpacing': `${message}`,
'notContainSpecialChar': `${message}`,
}
```
Creating custom validation rules.
```js
import React, { Component } from 'react'
import InputField, { bindFormValidation } from 'redux-form-manager'
const createForm = (state) => {
return {
firstname: {
name: 'firstname',
type: 'input',
label: 'Firstname',
value: '',
placeholder: 'write down your firstname',
disabled: false,
hidden: false,
rules: {
required: 'Please fill in your firstname.'
customValidate: [
{
message: 'ชื่อต้องขึ้นต้นด้วยตัวเลข',
valid: value => {
return /^\d/.test(value)
}
}
]
},
},
lastname: {
name: 'lastname',
type: 'input',
label: 'Lastname',
value: '',
placeholder: 'write down your lastname',
disabled: false,
hidden: false,
rules: {
required: 'Please fill in your lastname.'
},
}
}
}
const core = {
actionType: 'FORM/CHANGE/CUSTOMER',
formData: (state, props) => createForm(state),
renderUIInputField: (fieldData, updateValue) => {
return <InputField {...fieldData} onChange={updateValue} />
}
}
@bindFormValidation(core)
```
## <a id="feature-nested-field"></a>Nested Field Data
Using Nested field data such as array of object.
## <a id="feature-dynamic-field-props"></a>Dynamic Field Data properties
Handling dynamic field properties.
## <a id="feature-dispatch-firsterror"></a>Dispatch firsterror
Dispatch firstError to outside container.
## <a id="feature-other-dispatch"></a>Other Dispatchs
Using other event to dispatch.
## Contribution
### just clone and pull request.
#### Please open any issues, I will solve it as fast as possible.
#### Contact me mondit.thum@gmail.com
Thank you for your support !