@rexlabs-spicerhaart/form v3.0.13
Form - Vivid Component
A form abstraction built on top of @aaronuu/react-forms
to track the state of forms locally.
There is two ways to use this component
ReactForms
component with a render prop (recommended)withForm({ ...config })
higher order component
The withForm({ ...config })
HOC is just a light wrapper around the ReactForms
component, just with less flexibility (it is mostly included for compatibility reasons)
The <Form />
component is a light wrapper around a basic HTML form that adds the neccessary event handlers and also lets you give a FormError
component as a prop to display all of the form errors as a group, if needed.
The <Field />
components are responsible for mapping the form state to the input component within that field. The name
given to the <Field />
will be the key of the values object in state (nested object and array names are supported - a FieldArray
component is exposed to help with array fields).
Validation can be achieved by giving a validation function to the validate
prop of ReactForms
or as a property on the configuration options given to the withForm({ ...config })
HOC. Validating this way is a form level concern, with individual errors being passed down from the formik context to the fields individually, instead of each field being able to validate itself. Each Field
has it's own validate prop that will be used to validate individual fields and if set will be used instead.
Note - In certain circumstances (where the individual field validation is synchronous and the outer form validation is asynchronous) then the resolved error will override the synchronous error, but only when the form is submitted.
The asyncValuesReady
prop will reset the form when this value changes from false to true the very first time.
API
Form
FormError: React Component
This component will receive an object of errors under an error prop but only if there are errors
Field
name: String
Name that the field will be referred by internally, can be flattened object or array notation (i.e. nested.name.0
)
label: String
Label that will be given to the field
isInlineLabel: boolean
Whether or not to inline the label
inputProps: Object
Object that will be spread over the input component
Input: React Component
Input component to render
FieldLabel: React Component
Field label component to render
FieldError: React Component
Field error component to render
onFocus: Function
Function to run when the field is focused
onChange: Function
Function to run when the field is changed
onBlur: Function
Function to run when the field is blurred
FieldArray
Takes all the same props as <Field />
and will give them to the <Field />
if the user spreads the return value of the getFieldProps
function on to the field
Examples
ReactForms
import React, { Component } from 'react';
import { ReactForms, Form, Field } from '@rexlabs/form';
@entityModel
class FormParent extends Component {
render() {
return (
<ReactForms>
{({ values, errors, touched, setValues /* etc. */ }) => (
<Form>
<Field
name="address.line_1"
label="Address line 1"
Input={TextInput}
/>
<Field
name="address.line_2"
label="Address line 2"
Input={TextInput}
/>
<Field name="address.postcode" label="Postcode" Input={TextInput} />
</Form>
)}
</ReactForms>
);
}
}
withForm({ ...config })
import React, { Component } from 'react';
import {
withForm,
Form,
Field,
FieldArray
} from '@rexlabs/form';
@entityModel // Entity HOC that needs to pass initial state to form should be above withForm
@withForm({
name: 'MyForm',
handleSubmit: (values, {
props,
setValues,
setFieldValue
setErrors,
setFieldError,
setTouched,
setFieldTouched,
setStatus,
resetForm,
submitForm
}) => {
// submit action here
return props.entityModel.create(values);
},
mapPopsToValues: props => ({
// return initial state object here
email: props.entity.email
}),
validate: (values, props) => {
const errors = {};
if (!values.email) {
errors.email = 'Email is required';
}
return errors;
},
validateOnBlur: true,
validateOnChange: false,
// asyncValuesReady function runs each render until it returns true
// this will ensure that when your async content loads the form
// is populated with those initial values
asyncValuesReady = props => props.entity.status === 'loaded'
})
class FormParent extends Component {
render () {
return (
<Form>
<Field
name="email"
label="Email"
onBlur={e => {
// action to take on input blur
this.props.Test.submit();
}}
onChange={e => {
// action to take on input change
this.props.Test.change('emailClone', e.target.value);
}}
Input={TextInput}
inputProps={{
placeholder: 'Enter email',
debounce: 0
}}
/>
<Field
name="emailClone"
label="Email Clone"
Input={TextInput}
inputProps={{
placeholder: 'Enter email',
debounce: 0
}}
/>
{/* Add a default field array for dynamic fields */}
<FieldArray
name='friends'
Input={TextInput}
inputProps={{
placeholder: 'Add friend'
}}
>
{/* FieldArray will accept a render prop for more custom behaviour */}
<FieldArray
name='friends'
Input={TextInput}
inputProps={{
placeholder: 'Add friend'
}}
>
{({ fields, push, pop, swap, move, insert, unshift, remove, getFieldProps }) => (
<div>
{_.map(fields, (field, index) => (
<div key={field.id}>
<Field
{...getFieldProps(field, index)}
/>
<button onClick={() => { remove(index); }}>Remove</button>
</div>
))}
<button
onClick={() => {
push({
// Custom options for each field pushed will be merged with options
// given to FieldArray with individual overrides taking preference
Input: Checkbox,
validate: val => !val ? 'Must be checked' : null
})
}}
>
Add new
</button>
</div>
)}
</FieldArray>
</Form>
);
}
}
Development
Install dependencies
$ yarn
Available Commands
$ yarn start # starts storybook, for visually testing form
$ yarn test # runs all units tests
$ yarn test:watch # runs unit tests when files change
$ yarn build # bundles the package for production
Legal
Copyright © 2018 Rex Software All Rights Reserved.
4 years ago