react-state-form
Version:
188 lines (158 loc) • 4.6 kB
JSX
import React, { Component } from 'react'
import PropTypes from 'prop-types' // eslint-disable-line import/no-extraneous-dependencies
import validations from './utils/validationRules'
/**
* Main Component, a HOC, will store all fields' state.
*/
export default class FormContainer extends Component {
constructor(props) {
super(props)
const {
isDisabled = false,
shouldValidateForm,
} = this.props
const defaultState = {
isDisabled : false,
shouldValidateForm : true,
errors : {}
}
this.state = {
...defaultState,
shouldValidateForm,
isDisabled
}
}
getChildContext() {
return {
setFieldValue : ({ event, field, value, isMultipleValues, id = 'value' }) => {
const fieldName = field.id
const setState = (name, fieldValue) => {
this.setState((prevState) => ({
...prevState,
fields : {
...prevState.fields,
[fieldName] : {
...prevState.fields[name],
shouldValidateField : true,
[id] : fieldValue
}
}
}))
}
if (!value) {
setState(fieldName, event.currentTarget.value)
} else if (isMultipleValues) {
const stateCopy = { ...this.state }
for (const item in value) {
stateCopy.fields[item].shouldValidateField = true
stateCopy.fields[item][id] = value[item]
}
this.setState((prevState) => ({
...prevState,
...stateCopy
}))
} else {
setState(fieldName, value)
}
return this.state
},
addField : (data) => {
this.setState((prevState) => ({
...prevState,
fields : {
...prevState.fields,
[data.id] : {
...data,
shouldValidateField : false
}
}
}))
},
validateForm : (fieldName) => {
const { shouldValidateForm, fields } = this.state
const field = fields[fieldName]
if (shouldValidateForm) {
if (field && field.shouldValidateField) {
this.validateField(field)
return
}
for (const field in fields) {
const fieldData = fields[field]
if (fieldData) {
this.validateField(fieldData)
}
}
}
return
},
formData : this.state
}
}
/**
* React Lifecycle Method: Renders the data
*
* @return {DOM} Main container DOM.
*/
render() {
return (
<form action=''>
{this.props.children}
</form>
)
}
validateField = (fieldData) => {
const {
id,
label,
value,
validate,
displayName,
customRules = {}
} = fieldData
const rules = validate.split('|')
if (rules.length) {
for (const rule in rules) {
const ruleName = rules[rule]
const ruleDetails = ruleName.split('-')
const [ ruleValue, ...ruleArgs ] = ruleDetails
const validation = validations[ruleValue] || customRules[ruleValue]
if (validation) {
const result = validation.rule.apply(null, ruleArgs).test(value)
let error = ''
if (!result) {
error = validation.formatter.apply(null, [ displayName || id, ...ruleArgs])
}
this.setState((prevState) => ({
...prevState,
errors : {
...prevState.errors,
[id] : error
}
}))
if (error) {
break
}
} else {
throw `invalid validation rule: ${ruleValue}, please use an existing validation rule name or pass a custom function with same name through 'customRules' prop in Input: ${fieldData.id}. Rule value should be an object with keys: 'rule' as an Regex and 'formatter' as a function, that formats the value.`
}
}
}
return ''
}
static defaultProps = {
children : <div />,
isDisabled : false,
shouldValidateForm : true
}
static propTypes = {
children : PropTypes.node.isRequired,
isDisabled : PropTypes.bool,
shouldValidateForm : PropTypes.bool
}
static childContextTypes = {
addField : PropTypes.func,
setFieldValue : PropTypes.func,
validateForm : PropTypes.func,
formData : PropTypes.object
}
}