Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/balazsorban44/use-form
A React hook 🎣 for easy form handling
https://github.com/balazsorban44/use-form
custom-hook form form-validation form-validation-react react react-hook
Last synced: 15 days ago
JSON representation
A React hook 🎣 for easy form handling
- Host: GitHub
- URL: https://github.com/balazsorban44/use-form
- Owner: balazsorban44
- License: mit
- Created: 2019-07-08T10:58:09.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2023-05-06T06:04:01.000Z (over 1 year ago)
- Last Synced: 2024-10-10T17:34:39.881Z (28 days ago)
- Topics: custom-hook, form, form-validation, form-validation-react, react, react-hook
- Language: JavaScript
- Homepage: https://npm.im/another-use-form-hook
- Size: 2.34 MB
- Stars: 10
- Watchers: 1
- Forks: 0
- Open Issues: 11
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
another-use-form-hook
A React hook 🎣 for easy form handling
---
[![Build Status][build-badge]][build] [![Code Coverage][coverage-badge]][coverage] [![version][version-badge]][package] [![downloads][downloads-badge]][npmtrends] [![Total downloads][total-downloads]][total-downloads] [![MIT License][license-badge]][license] [![PRs Welcome][prs-badge]][prs] [![Code of Conduct][coc-badge]][coc]
[![Watch on GitHub][github-watch-badge]][github-watch] [![Star on GitHub][github-star-badge]][github-star]
---
## Table of Contents
- [Table of Contents](#table-of-contents)
- [Installation](#installation)
- [Usage](#usage)
- [Documentation](#documentation)
- [useForm](#useform)
- [UseFormParams](#useformparams)
- [InitialState](#initialstate)
- [Validators](#validators)
- [Validations](#validations)
- [SubmitCallback](#submitcallback)
- [NotifyCallback](#notifycallback)
- [UseFormReturn](#useformreturn)
- [FieldValuesAndErrors](#fieldvaluesanderrors)
- [ChangeHandler](#changehandler)
- [SubmitHandler](#submithandler)
- [InputPropGenerators](#inputpropgenerators)
- [InputTypeOptions](#inputtypeoptions)
- [InputPropGeneratorsReturn](#inputpropgeneratorsreturn)
- [FormProvider](#formprovider)
- [FormProviderProps](#formproviderprops)
- [getForms](#getforms)
- [Forms](#forms)
- [LICENSE](#license)## Installation
This should be installed as one of your project `dependencies`:```
yarn add another-use-form-hook
```
or
```
npm install --save another-use-form-hook
```> NOTE: `another-use-form-hook` only works with [**react >=16.8**][react-hooks], since it is a hook.
## Usage
This hook is intended to give a full solution for handling forms. From interdependent field value validations (meaning if a field value is dependent on other field value), to submitting the form, and providing information about when the UI should be unresponsive (loading of some kind of async-like operation), in addition to notification "hooks" to be able to inform the users the most efficient way.
To retrieve props for an input field, you have the following options:
1. Using the `form.inputs.{inputType}('name')` input prop generator function (`inputTypes` is one of [these][input-types-mdn])
2. Using `form.fields.{name}.{value|error}` and `form.handleChange` functions> NOTE: The example below is available live at [CodeSandbox][codesandbox-example]
>
Let's see a complex example to understand how it works:```jsx
import React from "react";
import ReactDOM from "react-dom";
import useForm from "another-use-form-hook";
import "./styles.css";
/**
* NOTE: We are using date-fns for this example,
* but it is absolutly not a requirement.
*/
import { addDays, isAfter, differenceInDays, parseISO } from "date-fns";
const isValidEmail = email => /\w*@\w*\.\w*/.test(email); // Do better 💩const TODAY = new Date();
const App = () => {
const form = useForm({
initialState: {
email: "",
arrival: TODAY,
departure: TODAY
},
validators: (fields, isSubmitting) => ({
// if not submitting, don't throw errors for invalid e-mail, like an empty field
email: isSubmitting
? isValidEmail(fields.email)
: typeof fields.email === "string",
// earliest arrival must be tomorrow
arrival: isAfter(parseISO(fields.arrival), TODAY),
// earliest departure must be after tomorrow
departure: isAfter(addDays(parseISO(fields.departure), 1), TODAY),
// departure must be at least a day after arrival
minOneNight: isSubmitting
? differenceInDays(
parseISO(fields.departure),
parseISO(fields.arrival)
) >= 1
: true
}),
onNotify: (type, reason) => {
// you can use type and reason to send specific notifications to the user
switch (type) {
case "submitError":
console.error("Form could not be submitted: ", reason);
break;
case "submitSuccess":
console.info("Form has been submitted.", reason);
break;
case "validationErrors":
console.warn(
"The following problems occurred while validating: ",
reason
);
break;
default:
break;
}
},
onSubmit: async ({ fields, setLoading, notify }) => {
try {
setLoading(true);
// submitting the form, eg.: fetch("path/to/my/submit")
const response = await new Promise(resolve => {
setTimeout(() => {
console.log("Submitting: ", fields);
resolve(fields);
}, 1000);
});
notify("submitSuccess", response);
} catch (error) {
notify("submitError", error.message);
} finally {
setLoading(false);
}
}
});return (
{/* option 1 (control all the props with a one-liner)*/}
Option 1
{form.fields.email.error ? "Invalid" : ""} email
{form.fields.arrival.error ? "Invalid" : ""} arrival
form.handleChange(e, ["minOneNight"])}
/>
{/* option 2 specify id, type, name, value props manually*/}
Option 2
{form.fields.arrival.error ? "Invalid" : ""} arrival
form.handleChange(e, ["minOneNight"])}
/>
{form.fields.departure.error ? "Invalid" : ""} departure
form.handleChange(e, ["minOneNight"])}
/>
{/* also from option 1 */}
Submit
);
};ReactDOM.render(, document.querySelector("#root"));
```## Documentation
### useForm
```ts
useForm(useFormParams: UserFormParams): UseForm
```#### UseFormParams
| name | type | description |
| :----------- | :--------- | -------------------------------------------------------------------: |
| name | `string` | Refer to one of the forms in [formProviderProps](#formproviderprops) |
| initialState | `object` | See [InitialState](#initialstate) |
| validators | `function` | See [Validators](#validators) |
| onSubmit | `function` | See [SubmitCallback](#submitcallback) |
| onNotify | `function` | See [NotifyCallback](#notifycallback) |##### InitialState
An object containing the default value of every field in a form.
> Example:
```js
useForm({
initialState: {
email: "",
name: "",
address: "",
age: 0
}
})
```If `name` is defined, you can refer to `initialStates.{name}` in [formProviderProps](#formproviderprops).
> Example:
```jsx
//...//...
const form = useForm({name: "login"})
console.log(form.fields.email.value) // [email protected]
```##### Validators
This function is invoked before [onChange](#changecallback) and [onSubmit](#submitcallback). The former only runs the validations for the changed fields, while the latter runs it on the whole form. For convenience, it is also returned from `useForm`.
```ts
function validators(fields: object, isSubmitting: boolean): Validations
```| name | type | description |
| :---------- | :-------- | ---------------------------------------------------------------: |
| fields | `object` | An object with the same shape as [initialState](#initialstate) |
| submitting | `boolean` | Set to `true`, when called before [handleSubmit](#submithandler) |
| validations | `object` | See [Validations](#validations) |###### Validations
An object containing `boolean` expressions. Each input field must have at least a corresponding property in this object, but you can define custom ones as well.
> Example:
```js
{
email: submitting
? isValidEmail(fields.email)
: typeof fields.email === "string"
}
```
You can also look at the [live example](#usage).##### SubmitCallback
Invoked when [handleSubmit](#submithandler) is called and there were no validation issues.```ts
function onSubmit(onSubmitParams: OnSubmitParams): void
```| name | type | description |
| :--------- | :--------- | ----------------------------------------------------------------: |
| name | `string` | Same as `name` in [useFormParams](#useformparams) |
| fields | `object` | Validated fields, same shape as [initialState](#initialstate) |
| setLoading | `function` | Sets the returned `loading` property of [useForm](#useformreturn) |
| notify | `function` | See [NotifyCallback](#notifycallback) |##### NotifyCallback
Invoked if there is a validation error when calling `handleChange` or `handleSubmit`. Can be manually triggered on `onSubmit` by calling `notify`.```ts
type NotifyType = "validationErrors" | "submitError" | "submitSuccess"
function notify(type: NotifyType, reason: any): void
```| name | type | description |
| :----- | :------- | -------------------------------------------------------------------------------------------------------: |
| type | `string` | Type of notification |
| reason | `any` | When type is `validationErrors`, it is a list of field names, Otherwise you set it to whatever you want. |> Example:
Look at the [live example](#usage).
#### UseFormReturn
| name | type | description |
| :----------- | :--------- | ----------------------------------------------------------------------------: |
| name | `string` | Same as in [useFormParams](#useformparams). |
| fields | `object` | See [FieldValuesAndErrors](#fieldvaluesanderrors) |
| hasErrors | `boolean` | For convenience. `true` if any of the returned fields.{name}.error is `true`. |
| handleChange | `function` | See [ChangeHandler](#changehandler) |
| handleSubmit | `function` | See [SubmitHandler](#submithandler) |
| loading | `boolean` | Controlled by `setLoading` in [onSubmit](#submitcallback) |
| inputs | `object` | See [InputPropGenerators](#inputpropgenerators) |
| validators | `function` | See [Validators](#validators) |##### FieldValuesAndErrors
Validated field values and their errors.
> Example:```js
const form = useForm({initialState: {
email: "[email protected]",
age: -2
}})
console.log(form.fields)
// {
// email: {
// value: "[email protected]",
// error: false
// },
// age: {
// value: -2,
// error: truefields
// }
// }
console.log(form.hasErrors) // true
```
##### ChangeHandler
You can call this two ways. Either pass an event as the first argument, or a partial `fields` object. With the latter, you can change multiple values at the same time. E.g.: resetting the form after submit, or any other reason you might have.
```ts
function handleChange(event: React.FormEvent, validations: string[]): void
function handleChange(fields: object, validations: string[]): void
```
| name | type | description |
| :---------- | :---------------- | ---------------------------------------------------------------------------------------------------------------: |
| event | `React.FormEvent` | Standard event. Using `target.{name\|value\|checked}` to infer the intention |
| fields | `object` | Pass a partial `fields` object, if you want to change multiple values at the same time |
| validations | `string[]` | Which `validators` you would like to run. If omitted, only validators with the same event/field name will be run |> Example:
Look at the [live example](#usage).
##### SubmitHandler
Call to submit the form. Before [onSubmit](#submitcallback) is invoked, [validators](#validators) is run for every form field. If there were any errors, [notify](#notifycallback) is invoked with `type` being `validationErrors`, and `reason` a list of form field names.```ts
function handleSubmit(): void
```##### InputPropGenerators
An object, containing properties with the same name as the [HTML input types][input-types-mdn], with some minor differences.
For convenience, since `datetime-local` contains a hyphen (-) character, it is also exposed as `datetimeLocal`, to overcome the need of `"` characters, when accessing it.
I.e.:
```js
const form = useForm()
form.inputs.datetimeLocal == form.inputs["datetime-local"]
```
In addition to the standard input types, there is a `select` type also available.Each property is a function:
```ts
function inputType(name: string, options: InputTypeOptions): InputPropGeneratorsReturn
```| name | type | description |
| :------ | :------- | ---------------------------------------------------------------------------: |
| name | `string` | The name of a field. Same as the properties of [initialState](#initialstate) |
| options | `object` | See [InputTypeOptions](#inputtypeoptions) |> Example:
For examples of all types, you can check [this test suite][input-prop-generator-test-file]
###### InputTypeOptions
An optional object| name | type | description |
| :------------ | :--------- | -----------------------------------------------------------------------------------------------------------------------------------: |
| value | `string` | Usually, when using radio buttons, values are static. (Each button in the same group must have different values) |
| generateProps | `function` | Provides `name`, `value` and `error` that can be used to generate additional props. Useful, if you want to avoid using `form.fields` |
| formName | `string` | If the input type is `submit`, it can be used to override the name of the form being submitted. |> Example:
1.
```jsx
const form = useForm(/*...*/)
// *click on option-2*
console.log(form.fields.radio.value) // option-2
return (
{/*You can do it the "traditional" way also*/}
)
```
2.
```jsx
const form = useForm(/*...*/)
return(
({
className: error ? "email-error" : "email",
})
})}
/>
{/*Tip: if your custom Input component takes an error prop, you can try this: */}
_})}/>
{/* This will spread error to Input as well.*/}{/*Or here is a more complex example for a custom Input component*/}
({
error,
label: error ? `Invalid ${name}` : name,
placeholder: `Type ${name}`
})
})}
/>
)
```###### InputPropGeneratorsReturn
An object that can be spread on a React input like element.| name | type | description |
| :------- | :--------- | ----------------------------------------------------------------------------------------------------------------------------------------------: |
| name | `string` | The name of a field. Must be one of the properties in [initialState](#initialstate) |
| id | `string` | By default, same as `name`. If input type is `radio`, it is the same as `value`, to avoid problems in radio groups where `id` would be the same |
| value | `any` | The value of the field |
| onChange | `function` | See [onChange](#changehandler) |
| checked | `boolean` | If input type is `checkbox`, it is the same as `value` |
| onClick | `function` | If input type is `submit`, it is [onSubmit](#submithandler) |> Example:
```jsx
const form = useForm(/*...*/)return (
{/* This is the same as */}
)
```### FormProvider
```ts
function FormProvider(formProviderProps: FormProviderProps): JSX.Element
```#### FormProviderProps
| name | type | description |
| :------------ | :------------- | ------------------------------------------------------------------------------------------: |
| initialStates | `object` | Single place to define all `initialState` for every form. See [InitialState](#initialState) |
| validators | `object` | Single place to define all `validators` for every form. See [Validators](#validators) |
| onSubmit | `function` | Same as [onSubmit](#submitcallback) |
| onNotify | `function` | Same as [onNotify](#notifycallback) |
| children | `ReactElement` | The element you would like to wrap with `FormProvider` |### getForms
```ts
function getForms() : Forms
```#### Forms
An object containing all the forms' current values in [FormProvider](#formprovider). Same shape as `initialStates`.---
## LICENSE
MIT
[build-badge]:
https://img.shields.io/travis/balazsorban44/use-form.svg?style=flat-square
[build]: https://travis-ci.org/balazsorban44/use-form
[coverage-badge]:
https://img.shields.io/codecov/c/github/balazsorban44/use-form.svg?style=flat-square
[coverage]: https://codecov.io/github/balazsorban44/use-form
[version-badge]:
https://img.shields.io/npm/v/another-use-form-hook.svg?style=flat-square
[package]: https://www.npmjs.com/package/another-use-form-hook
[downloads-badge]:
https://img.shields.io/npm/dm/another-use-form-hook.svg?style=flat-square[npm]: https://www.npmjs.com/
[node]: https://nodejs.org[npmtrends]: http://www.npmtrends.com/another-use-form-hook
[license-badge]:
https://img.shields.io/npm/l/another-use-form-hook.svg?style=flat-square
[license]:
https://github.com/balazsorban44/use-form/blob/master/LICENSE
[prs-badge]:
https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
[prs]: http://makeapullrequest.com
[donate-badge]:
https://img.shields.io/badge/$-support-green.svg?style=flat-square
[coc-badge]:
https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square
[coc]:
https://github.com/balazsorban44/use-form/blob/master/CODE_OF_CONDUCT.md
[github-watch-badge]:
https://img.shields.io/github/watchers/balazsorban44/use-form.svg?style=social
[github-watch]: https://github.com/balazsorban44/use-form/watchers
[github-star-badge]:
https://img.shields.io/github/stars/balazsorban44/use-form.svg?style=social
[github-star]: https://github.com/balazsorban44/use-form/stargazers
[react-hooks]: https://reactjs.org/blog/2019/02/06/react-v16.8.0.html
[input-types-mdn]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input
[codesandbox-example]: https://codesandbox.io/s/another-use-form-hook-2mler
[input-prop-generator-test-file]: https://github.com/balazsorban44/use-form/blob/master/src/__tests__/useForm-with-input-prop-generator.test.js
[total-downloads]: https://img.shields.io/npm/dt/another-use-form-hook.svg?label=total%20downloads&style=flat-square