rofrischmann/react-controlled-form

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# Controlled Forms

react-controlled-form aims to **simplify form management** in React.<br>
It ships functional APIs to create your very own form fields and is built with **flexibility** and **customization** in mind.<br>

It allows you to **bring your own components**.<br>
You do not have to struggle with predefined input components ever again!<br>
It only uses React Hooks under-the-hood and is thus super fast.

<img alt="npm downloads" src="https://img.shields.io/npm/dm/react-controlled-form.svg"> <img alt="npm version" src="https://badge.fury.io/js/react-controlled-form.svg">

## Installation

```sh
yarn add react-controlled-form
```

> Controlled Forms requires `react>=16.3.0` to be installed in your project.

## Benefits

- simple functional API
- Controlled state using `useState`
- full flexibility
- custom form fields
- reactive forms

## The Gist

```jsx
import { useField, useForm } from 'react-controlled-form'

function Input({ isValid, errorMessage, ...props }) {
  return (
    <div>
      <input style={{ borderColor: isValid ? 'black' : 'red' }} {...props} />
      {errorMessage && <div>{errorMessage}</div>}
    </div>
  )
}

const nameValidation = {
  'Please enter at least 2 characters.': (value) => value.length >= 2,
  'Only use alphabetic letters.': /^[a-z]*$/gi,
}

function Form() {
  const firstname = useField({
    name: 'firstname',
    validation: nameValidation,
  })

  const lastname = useField({
    name: 'firstname',
    validation: nameValidation,
  })

  const { submit, reset } = useForm(firstname, lastname)

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault()

        submit((isValid, data) => {
          if (isValid) {
            console.log('Submitted: ', data)
            reset()
          }
        })
      }}>
      <Input {...firstname.props} />
      <Input {...lastname.props} />
      <input type="submit" />
    </form>
  )
}
```

## API

### useField(options)

This hook uses `React.useState` under-the-hood and controls the state changes and validation for each field.
The internal representation of a field contains the following properties:

- value
- isValid
- isTouched
- isDisabled
- isRequired
- isLoading
- errorMessage

#### Options

| Option           |  Description                                                                                                   |
| ---------------- | -------------------------------------------------------------------------------------------------------------- |
| value            | The initial `value`.                                                                                           |
| touched          |  The initial `isTouched` value.                                                                                |
| loading          |  The initial `isLoading` value.                                                                                |
| disabled         |  The initial `isDisabled` value.                                                                               |
| required         |  The initial `isRequired` value.                                                                               |
| validation       |  An map of errorMessage-validator where validator can either be a function of value or a RegExp.               |
| showValidationOn |  When the field is "touched" and isValid / errorMessage are passed to the props.<br>Can be `change` or `blur`. |

#### Returns

`(Object)` An object containing the following properties:

```js
const shape = {
  // Those can be passed to your custom input implementation
  props,
  // A function used to update manually the field (use with caution)
  // It either takes properties from the internal field listed above
  // or a function of the current field that returns the new field
  update,
  initial,
  name,
  value,
  isValid,
  isTouched,
  isDisabled,
  isRequired,
  errorMessage,
}
```

### useForm(...fields)

This hook takes a list of fields, where a field is the output of the useField hook.

#### Returns

`(Object)` An object containing the following properties:

```js
const shape = {
  // Takes a function of (isValid, data) where isValid is the global validation state and data is a map of name-value
  // Calling submit will automatically touch all fields to reveal the error messages (if not already shown)
  submit,
  // Resets the form to its initial state
  reset,
  // Touches each field to reveal their validation state & error messages
  touchFields,
}
```

### useFormDebugger(...fields)

This hook is only meant for debugging reasons.
It takes the same fields as `useForm`, but returns all the field data on every render.

#### Returns

(Object) An object containing the following properties:

```js
const shape = {
  // A map of name-value pairs
  data,
  // A map of name-field pairs, where field represents the full internal representation of each field
  fields,
  // The global validation state which is accumulated by checking each field's isValid
  isValid,
}
```

---

### createUseField(resolveProps)

This factory function can be used to create your very own version of `useField` which can be useful if you want to implement different behaviour or return different props.

It takes a function that receives an object with the following shape:

```js
const params = {
  // All values from the internal field representation
  field,
  // A function used to validate the value according to the passed validation object
  validate,
  // The update function which is also returned by useField and described above
  update,
  // Any additionally passed options that are not directly part of the field representation e.g. showValidationOn
  options,
}
```

#### Usage

```js
import { createUseField } from 'react-controlled-form'

function resolveProps({ field, update, validate, options }) {
  const { name, value, isValid, isDisabled, isRequired } = field

  return {
    value,
    name,
    required: isRequired,
    disabled: isDisabled,
    onChange: (e) =>
      update({
        value: e.target.value,
        isValid: validate(e.target.value),
      }),
    style: {
      borderColor: isValid ? 'black' : options.validationColor,
    },
  }
}

const useField = createUseField(resolveProps)

// Usage
const firstname = useField({
  name: 'firstname',
  validationColor: 'pink',
  validation: {
    'Enter at least 2 characters.': (value) => value.length >= 2,
  },
})

// this will render an input with pink borders for invalid values
const Firstname = () => <input {...firstname.props} />
```

## Examples

- [Simple Example](examples/simple)

## License

react-controlled-form is licensed under the [MIT License](http://opensource.org/licenses/MIT).<br>
Created with ♥ by [@robinweser](http://weser.io) and all the great contributors.