JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 33
  • Score
    100M100P100Q75948F
  • License MIT

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>
  );
}

Try It on CodeSandbox



Example Code

Check out the Example App

CodeSandbox Examples



API

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.

explaining pathform to people who prefer other form libraries

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