Let's make forms a little less painful shall we?
<Form
onSubmit={({ values, formValid, resetInputs }) => {
if (formValid) {
makeApiCall(values);
} else {
resetInputs(['password']);
}
}}
>
<Input name="user.firstName" />
<Input name="user.lastName" />
<Input name="email" />
<Input name="password" />
<button type="submit">
Submit
</button>
</Form>
npm install @jbknowledge/react-form
Due to some limitations of react-native
, form submit events are only supported by the web version of this package at this time. In addition, you should always import from the native
submodule when developing in react-native
. For example:
import { Form, withFormHandling } from '@jbknowledge/react-form'; // This will not work for react-native projects
import { Form, withFormHandling } from '@jbknowledge/react-form/native';
This library exports the following:
withFormHandling
Form
ValidationError
withFormHandling(Component, onChange)
This HoC will provide Component
with 4 props:
value
: The current value for the input
setValue
: A callback function which replace the existing form value
error
: The current error message associated with the input, or null
inputProps
: An object of meta values which are passed to all inputs within the form
const Input = ({ value, setValue, error, inputProps }) => (
<div className="form-group">
{error && inputProps.displayErrors && <div className="form-error">{error}</div>}
<input
className="form-input"
value={value}
onChange={e => setValue(e.target.value)}
/>
</div>
);
export default withFormHandling(Input);
In addition, components wrapped by withFormHandling
must be provided a name
prop, i.e.
<Input name="password" />
Error Handling
By default, react-form
does not provide any error handling. The second argument for the HoC withFormHandling
or onChange
is how you can declare validation rules for your components. onChange
is expected to be either a single callback or an array of callbacks and will be called automatically by react-form
everytime the value changes. The first arg value
will be the next value while props
will be all props passed to your wrapped component. This function when called sets the value of error
on your behalf. Any error thrown by the onChange
callback you provide will automatically be caught and passed to your component via the error
prop.
export default withFormHandling(Input, (value) => {
throw new Error(); // props.error will be set to the thrown error
});
In the event your component has one or more anticipated errors, you can take advantage of the custom ValidationError
provided by react-form
as a convenience.
export default withFormHandling(Input, (value) => {
if (isNaN(value)) {
throw new ValidationError('Please enter a number.'); // props.error will be set to the string provided
}
});
In addition to value
, the onChange
callback is also provided all component props. This will allow you to achieve validation similar to the following:
export default withFormHandling(Input, (value, { regex }) => {
if (regex && !value.match(regex)) {
throw new ValidationError('Invalid pattern.')
}
});
If you provide an array of callback functions, each will run until an error is thrown.
export default withFormHandling(Input, [
() => throw new ValidationError('Invalid'),
someValidationFunction // never called because the first callback always errors
]);
Input Names
All components which are wrapped by withFormHandling
are required to define a value for the name
prop.
<Input name="password" />
This requirement serves two purposes:
- it allows
react-form
to uniquely identify inputs for state management - it allows you to define the structure of the resulting form values
name
can be anything, however, it is recommended that you mimic your api's schema. For example, with the following schema:
{
"user": {
"name": <string>,
"age": <number>
},
"title": <string>,
}
You can define inputs such as the following:
<Form>
<Input name="user.name" />
<Input name="user.age" />
<Input name="title" />
</Form>
Form
All components wrapped by withFormHandling
must be nested underneath one of react-form
's Form
components like above. Wrapped inputs, however, do not need to be direct children of the Form
object; the following is also a valid example:
<Form>
<div>
<div>
<Input name="password" />
</div>
</div>
</Form>
Other than props supported by html's form
, you can provide the following props to the Form
component:
onSubmit({ formValid, values, resetInputs })
This callback function will be called anytime a submit event is fired within the Form
component.
NOTE: this callback is not currently supported for react-native
, use the onChange
callback instead.
formValid
: true or false based on all of the nested inputs' error
props.
values
: All form values; structure is based on the value of nested inputs' name
props.
resetInputs
: A callback function which will allow you to reset one or more inputs back to their default values. See the Resetting Inputs
section for more information.
For example, the following form:
<Form>
<Input name="nested.value" />
<Input name="value" />
</Form>
could call your provided onSubmit
callback with:
{
formValid: false,
values: {
nested: {
value: 'example1',
},
value: 'example2'
},
resetInputs: () => {...}
}
onChange({ formValid, values })
This callback function will be called anytime a form value changes.
formValid
: true or false based on all of the nested inputs' error
props.
values
: All form values; structure is based on the value of nested inputs' name
props.
resetInputs
: A callback function which will allow you to reset one or more inputs back to their default values. See the Resetting Inputs
section for more information.
An example of what this might look like can be seen in the onSubmit
section.
inputProps
This is a simple object that passes user defined props directly to the wrapped inputs. For example:
<Form
inputProps={{
displayErrors: false,
}}
>
This inputProps
block would be passed as is automatically to all inputs wrapped by withFormHandling
making the following possible:
const CustomInput = ({ value, error, setValue, inputProps }) => (
<div>
{ error && inputProps.displayErrors && <div>{error}</div>}
<input ... />
</div>
);
export default withFormHandling(CustomInput);
NOTE: react-form
does not perform any optimization on this prop. Guaranteeing referential equality of inputProps
is the responsibility of the user.
Resetting Inputs
In both of the provided Form
lifecycle hooks, onChange
and onSubmit
, you are able to reset the value of one or more inputs back to their default values via the provided resetInputs(patterns)
function.
patterns
: An array of glob patterns which will be matched with your inputs' names.
Given the following form:
<Form
onChange={() => {...}}
>
<Input name="username" defaultValue="johndoe34" >
<Input name="address.street" >
<Input name="address.city" >
<Input name="address.state" >
<Input name="address.zip" >
</Form>
-
resetInputs(['address.state', 'address.city'])
will reset the values ofaddress.state
andaddress.city
back to empty strings -
resetInputs(['username'])
will reset the value ofusername
tojohndoe34
-
resetInputs(['address.*'])
will reset the value of all inputs exceptusername
back to empty strings -
resetInputs()
which is the same asresetInputs(['*'])
will reset all inputs back to their default values
react-form
was built and is maintained by JBKLabs, JBKnowledge Inc's research and development team.
This package is licensed under Apache License, Version 2.0. See LICENSE for the full license text.