Package Exports
- react-pathform
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (react-pathform) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
react-pathform
Pathform was built to scratch an itch for recursive, nested, dynamic forms. Using paths as an array, we can spread dynamic paths around like butter.
We can derive a lot from the path as array... Why is this useful?
Quick Start
npm install --save react-pathform
import React from 'react';
import { PathFormProvider, PathForm, PathFormField } from 'react-pathform';
import { Button, TextField } from '@material-ui/core';
function App() {
return (
<PathFormProvider
initialRenderValues={{
nested: {
items: [{ name: "Joey Joe Joe Jr." }]
}
}}
>
<PathForm onSubmit={(values) => alert(JSON.stringify(values, null, 2))}>
<PathFormField
path={["nested", "items", 0, "name"]}
defaultValue=""
render={({ inputProps, meta }) => {
return (
<TextField
label="Name"
error={!!meta.error}
helperText={meta.error?.message}
{...inputProps}
/>
);
}}
/>
<Button type="submit">Submit</Button>
</PathForm>
</PathFormProvider>
);
}
Example Code
Check out the Example App
CodeSandbox Examples
API
Components
Hooks
PathFormProvider (required)
The store context provider for your form. You must place it at the root of your form to be able to use usePathForm
, and PathForm*
components.
The data in the store will remain until the PathFormProvider
is unmounted.
PathFormProvider initialRenderValues: values
The initial data for your form on the initial render only.
PathForm
Renders a browser native form
element and allows you to hook into onValidate
and onSubmit
.
PathForm onValidate: (values: any)
Called just before submitting. Throw an error to stop onSubmit
from triggering.
PathForm onSubmit: (values: any)
Called on successful submission after validation.
PathFormField
Binds to a value at the given path in the store from.
PathFormField path: PathFormPath
The path selector to the item in your store.
PathFormField defaultValue: any[]
The value use on the initial render, if the store item does not already exist.
PathFormField render(props: PathFormFieldProps)
The callback to render the field.
PathFormField validations: PathFormValidation[]
Register validations for this field.
<PathFormField
path={['person', 0, 'name']}
defaultValue=""
validations={[
{ type: 'required', message: 'This person must have a name.' },
{ type: 'maxLength', value: 16, message: 'This must be less than 16 characters.' },
]}
render={({ inputProps, meta }) => {
return (
<TextField
label="Name"
error={!!meta.error}
helperText={meta.error?.message}
{...inputProps}
/>
);
}}
/>
PathFormArray
Binds to an array at the given path in the store from. The render
callback will be called for
each item in the array.
Use the meta.uuid
on your root item key
.
<PathFormArray
path={['path', 'to', 'array']}
defaultValue={[]}
renderItem={({ arrayPath, itemPath, index, totalRows, meta }) => (
<div key={meta.uuid}>
<NameField key={meta.uuid} path={[...itemPath, 'name']} />
<button onClick={() => array.remove(arrayPath, index)} disabled={totalRows <= 1}>Delete</button>
</div>
)}
renderEmpty={() => <>No Items!</>}
/>
PathFormArray path: PathFormPath
The path selector to the item in your store.
PathFormArray defaultValue: any[]
The value use on the initial render, if the store item does not already exist.
PathFormArray renderItem(props: PathFormArrayItemProps)
The callback to render an item in the array.
PathFormArray renderEmpty(props: PathFormArrayEmptyProps)
The callback of what to render when the array is empty.
usePathForm
Use this hook from any child component scope to access the context of your form.
Returns the form context provider object
with helper functions:
const { setValue, setTouched, addError, clearError, array } = usePathForm();
setValue(path: PathFormPath, value: any)
Sets the store item value
at the given path
.
setTouched(path: PathFormPath, touched: boolean)
Marks the store item at the given path as touched
.
addError(path: PathFormPath, error: PathFormError)
Adds an error
at the given path
.
clearError(path: PathFormPath)
Clears an error
at the given path
.
array: PathFormArrayUtils
An object of utilities for mutating array items in your form.
const { array } = usePathForm();
append function(path: PathFormPath, item: any)
Appends an item
to the end of the array at given path
.
array.append(["deeply", "nested", "items"], { "name": "Santa's Little Helper" });
prepend function(path: PathFormPath, item: any)
Prepends an item
to the beginning of the array at given path
.
array.prepend(["deeply", "nested", "items"], { "name": "Santa's Little Helper" });
move function(path: PathFormPath, fromIndex: number, toIndex: number)
Moves an item
in the array at given path
, from the fromIndex
to the toIndex
. Useful for reordering items.
array.move(["deeply", "nested", "items"], 3, 4);
remove function(path: PathFormPath, index: number)
Removes an item
from the array at given path
at index
.
array.remove(["deeply", "nested", "items"], 2);
usePathFormValue
Returns an array of [value, meta]
at the given path
.
const [nameValue, nameMeta] = usePathFormValue(['person', 'name']);
const [ageValue, ageMeta] = usePathFormValue(['person', 'age']);
Types
PathFormPath
type PathFormPath = Array<string | number>;
const path: PathFormPath = ["deeply", "nested", "items", 0, "children", 0, "name"];
The path to an item in your form.
Strings imply object property.
Numbers imply array index.
PathFormInputProps
The input props to hook your store into your component.
type PathFormInputProps = {
name: string;
value: any;
onChange: (event?: any, value?: any) => any;
onBlur: (event?: any) => any;
}
PathFormValidation
type PathFormValidation =
| { type: 'required'; message: string }
| { type: 'minLength' | 'maxLength' | 'min' | 'max'; value: number; message: string }
| { type: 'regex'; value: string; flags?: string; message: string };
PathFormError
type PathFormError = {
type: string;
message: string;
value: any;
};
PathFormStoreMeta
type PathFormStoreMeta = {
uuid: string;
dirty: boolean;
touched: boolean;
error: null;
};
But Y Tho?
I have loved many form react form libraries (wow, holy nerd right?). I have gone from redux-form to react-final-form to formik to react-hook-form. They are all amazing libraries. This project aims to provide all the best things from each library: the global control of redux-form, the observable model of react-final-form, the api of formik, and the performance of react-hook-form.
These libraries use the native input property name
as a dot notation string to bind or select data:
const name = "deeply.nested.items[0].children[0].name";
Whereas this library derives the name
from a path
. The difference is,
you can easily spread arrays, not strings.
const parentPath = ["deeply", "nested", "items"]; // name = "deeply.nested.items"
const childPath = [...parentPath, itemIndex, "children"]; // name = "deeply.nested.items[0].children"
const deepPath = [...childPath, childIndex, "name"]; // name = "deeply.nested.items[0].children[0].name"
This makes nested / recursive form components much cleaner.
The internal form store wraps the form structure alongside meta
next to values.
Values in the store are either an object, array, or primitive.
Objects and Arrays can have both child items, but only array is iterable.
Primitive values cannot have any children.
Feature Checklist
Category | Criteria | Complete |
---|---|---|
README | Feature Checklist | ☑ |
README | Quick Start | ☑ |
README | Simple CodeSandbox | ☑ |
README | API | ☑ |
Library | onValidate | ☑ |
Library | onSubmit | ☑ |
Library | reset | ☐ |
Library | touched & validation | ☐ |
Library | Built in validation | ☑ |
Examples | Codesandbox | ☑ |
Examples | Example App | ☐ |
Bundle Size | Optimize UUID | ☐ |
Bundle Size | Optimize Lodash | ☐ |
README | Contributing | ☐ |