formtools
v0.1.0
Published
Higher order components for React to manage forms and fields
Downloads
2
Readme
formtools
npm install formtools --save
Higher order components (HoC) for React to manage forms and fields.
Overview
These components are usefull for handling simple or medium-complex forms, and when you don't want to use tools like redux to manage the state of your fields.
The 'Field' HoC adds common behaviour to a form field, mainly normalization, validation, and keeping the field 'controlled'. The goal here is to keep your field completely stateless.
The 'Form' HoC acts as a state container with some extra functionalities. These functionalities are passed down as props.
These components are very unopinionated on how to inplement them in your app. They also don't depend on each other, which make them suitable for test.
Features
- Keeps your fields presentational
- Small footprint
- Highly plexible and customizable
- State is managed for you
- Can be used with other tools easily
Basic usage
Field
// Field example A: just a controlled input
import React from 'react';
import { Field } from 'formtools';
function Input({ onChange, value }) {
return (
<input type="text" value={value} onChange={onChange} />
);
}
export default Field({
valueMap: event => event.target.value
})(Input);
// Field example B: more complex
import React from 'react';
import {
Field,
expect,
targetValue,
defaultFieldProps
} from 'formtools';
function Input({ error, success, label, setRef, ...props }) {
// determine the className for the input element
let cx;
if (error) cx = 'error';
if (success) cx = 'success';
return (
<div className="field">
<label htmlFor={props.name}>{label}</label>
<input
ref={setRef} // set ref, i.e. to call focus() in Form component
className={cx}
{...defaultFieldProps(props)} // helper which adds value, events, etc.
type="text"
/>
{error && <div className="errorMessage">{error}</div>}
</div>
);
}
export default Field({
valueMap: targetValue, // helper: event => event.target.value
normalize: val => val.toUpperCase(),
validate: [
// access child field properties within validation rules
expect((val, _, field) => field('name').value.length > 0, 'Please fill in your name first'),
expect(val => val.length >= 3, 'This value is to short'),
expect(val => val.length < 9, 'This value is to long')
]
})(Input);
// Form example:
import React, { Component } from 'react';
import { Form } from 'formtools';
import Zipcode from './Zipcode';
import checkZipcode from './api';
@Form()
class RegistrationForm extends Component {
handleSubmit = () => {
// function from HoC
const { validate, getValues, asyncError } = this.props;
// validate every field and display error / successs if possible
// return true of every field passes validation
if (!validate()) return;
const values = getValues();
// example: check with api if zipcode is valid
checkZipcode(values.zipcode)
.then(data => {
// on error manually overwrite error message of Zipcode field
if (!data) asyncError('zipcode', 'Invalid zipcode!');
// do something with all the field values :)
else someAction(values)
});
}
render() {
return (
<div>
<Zipcode name="zipcode">
... other fields
<button onClick={this.handleSubmit}>Submit</button>
</div>
)
}
}
Docs
Field(config: Object): (Component): Component
Typical decorator pattern. Usage:
// es6
const Enhanced = Field({})(Component);
// es7
@Field({})
config
All of the config settings are totally optional.
valueMap
A function which describes where to find the value in a onChange-handler. Typical usecase is 'event => event.target.value'.
normalize
A function which takes a value and transforms it into another value. I.e.:
val => val.toUpperCase()
validation
Can be a function or an array of functions. Must be used together with to expect function. I.e:
expect((value, ownProps, otherField) => value.length > 0, 'Error message on failure')
expect calls your validator function with 3 arguments:
- value: the value after normalization
- ownProps: specific properties your form gave this field
- field: a function which can be called with a field-name and returns an object containing value, error, success, dirty and touched
In case of multiple validators, the validation will stop on the first error, so it is wise to sort your validators by priority.
Injected props
These props will be passed to the wrapped component:
- Standard form events: onChange, onBlur, onFocus, onKeyDown, onKeyUp, onKeyPress,
- value
- name
- error: string if there's an validation error, boolean (false) if not
- success: boolean
- setRef: allows HoC to access the inputs dom-node (optional)
- Other props you've given..
Props
- name required
- Standard form events like onChange. All these event have a signature of (value, name)
- Special event onValidChange, which will be called as soon as a value passes all validation
- Own custom props, which will be passed down to the original wrapped component
- initValue: value to start with, defaults to ''
- aggressive: boolean which validates after each onChange instead of after the blur event
Validation behaviour
if no validators are set, success and error both will stay false
Validation will occur:
- after the onBlur event
- after the onChange event when the prop aggressive is set
- after the validate method of the Form HoC is used
Form
Typical decorator pattern. Usage:
// es6
const Enhanced = Form()(Component);
// es7
@Form()
Exposed methods via props:
getValues()
Returns an object with fieldnames as key and their corresponding values.
validate(x?: string | string[], soft: boolean = false)
Validates all fields by default, unless a fieldname or an array of fieldnames is given, and returns true if all fields passed validation, false otherwise. Soft mode is disabled by default. When set to true, validation happens silently in the background without rendering error/success
clear(x?: string | string[])
Resets all fields by default, unless a fieldname or an array of fieldnames is given. All values of the field will be '' (empty), and the props success, error, touched and dirty will be set to false.
reset(x?: string | string[])
Same as clear(), except that reset will set the value back to initValue if possible
asyncError(fieldname: string, errorMessage: string)
Manually overwrite an error message of a field after a async call.
asyncErrorClear(fieldname: string)
Clears to above error and sets the validation before the async-error was set.
node(fieldname: string)
Returns a dom-node of a field. See setRef prop in Field.
event(type: string, handler: (value: any, name: string))
Allows you to subscribe to common events. Usage:
this.props.events(
'onChange',
(value, name) => console.log(`The value of ${name} is '${value}'`)
);
field(fieldname: string)
Return an object with data:
- value
- success
- error
- touched: field was at least focussed once
- dirty: field was changed at least once