An open API service indexing awesome lists of open source software.

https://github.com/cupcakearmy/formhero

Fully customisable React form utility
https://github.com/cupcakearmy/formhero

form-helper form-validation forms hooks react

Last synced: about 1 year ago
JSON representation

Fully customisable React form utility

Awesome Lists containing this project

README

          

![Logo](https://raw.githubusercontent.com/cupcakearmy/formhero/master/.github/Logo.jpg)

![version badge](https://badgen.net/npm/v/formhero)
![types badge](https://badgen.net/npm/types/formhero)
![downloads badge](https://badgen.net/npm/dt/formhero)
![dependencies](https://badgen.net//bundlephobia/dependency-count/formhero)
![minzip size badge](https://badgen.net/bundlephobia/minzip/formhero)

**Fully customisable react form utility.**

## 🌈 Features

- Typescript compatible
- Customizable extractor, validator, getter and setters. (More in the docs)
- **0** Dependencies
- Tiny **~0.7kB**
- React Hooks

###### Installation

```
npm i formhero
```

_Note:_ Requires at least typescript version `3.5`, otherwise the error object will not have the right inherited types.

### 👁 Demos

- [**_Live Web_**](https://cupcakearmy.github.io/formhero/)
- [**_Live Codesandbox_**](https://codesandbox.io/embed/formhero-simple-bdcx2?expanddevtools=1&fontsize=14)
- [**_Live React-Native_**](https://snack.expo.io/@cupcakearmy/useform)

### 📖 Docs

- [Examples](#-examples-more-here)
- [Validation](#validation)
- [Easy Customization](#easy-customization)
- [Dynamic Fields](#dynamic-fields)
- [Docs](#-documentation)
- Contructor
- [Initial State](#initial)
- [Validators](#validators)
- [Options](#options)
- Returns
- [field](#field)
- [form](#form)
- [errors](#errors)
- [isValid](#isvalid)
- [setField](#setfield)
- [setForm](#setform)
- [setErrors](#seterrors)

## 🤔 Motivation & Why

So why write yet another form utility you might ask? First off, I don't like the Formik approach. In my humble opition formik is very verbose and requires lots of boilerplate. Also does not work with hooks. [react-hook-form](https://react-hook-form.com/) is a very cool library and it is the main inspiration for formhero. It does almost everything right... typescript, no deps, small, concise.

The problem that I found while using it was that 3rd party ui libs like [Ant Design](https://ant.design/) or [Fabric UI](https://developer.microsoft.com/en-us/fabric#/controls/web) do not always have the standart `onChange` or `value` props in their components. That is where react-hook-form starts falling apart. This is what formhero tries to address in the most minimalistic way possible, with as little code as needed. All in pure typescript and no deps.

## 🚀 Quickstart

```typescript
import ReactDOM from 'react-dom'
import { useForm } from 'formhero'

const Form = () => {
const { field, form } = useForm({
username: '',
password: '',
})

const _submit = (e: React.FormEvent) => {
e.preventDefault()
console.log(form)
}

return (




Go 🚀


)
}
```

## 🔥 Examples [(More Here)](https://github.com/CupCakeArmy/formhero/tree/master/examples)

### Validation

```typescript
const Form = () => {
const { field, form, errors } = useForm(
{
username: '',
email: '',
password: '',
},
{
username: (value) => value.length > 3,
email: {
validator: /@/,
message: 'Must contain an @',
},
password: [
{
validator: /[A-Z]/,
message: 'Must contain an uppercase letter',
},
{
validator: /[\d]/,
message: 'Must contain a digit',
},
],
}
)

return (

Errors & Validation


{errors.username && 'Must be longer than 3'}


{errors.email}


{errors.password}

)
}
```

### Easy Customization

Often it happens that you use a specific input or framework, so the default getter, setter and extractor for the event won't cut it. No worries: formhero got you covered!

```typescript
const Form = () => {
const { field, form, errors } = useForm({
awesome: true,
})

return (
{
e.preventDefault()
console.log(form)
}}
>

Custom


e.target.checked,
})}
/>
Is it awesome?



)
}
```

### Dynamic Fields

Sometimes you don't know all the fields upfront. You can simply define a generic type and assign it to the initial object. Of course type assistance is limited in this case as formhero cannot be sure what keys are valid.

```typescript
import React from 'react'
import ReactDOM from 'react-dom'
import { useForm } from 'formhero'

type MyForm = { [field: string]: string | number }

const init: MyForm = {
username: 'unicorn',
password: '',
}

const Form: React.FC = () => {
const { field, form, errors } = useForm(init)

return (





)
}
```

## 📖 Documentation

### `useForm`

```typescript
const { field, errors, update, form, isValid } = useForm(initial, validators, options)
```

### Initial

This is the base state of the form. Also the typescript types are inhered by this.

###### Example

```javascript
const initial = {
username: 'defaultValue',
password: '',
rememberMe: true,
}
```

### Validators

A validator is an object that taked in either a `RegExp` or a `Function` (can be async) or an array of those. Optionally you can pass a message string that will be displayed instead of the default one.

A validator functions takes the current value as input and should return a `boolean` or a `string`. If returned `true` the input counts as valid, if `false` it's not. If you pass a string formhero will treat it as not valid and display the string returned as error message.

###### Example: Regular Expression

```javascript
const validators = {
// Only contains letters.
// This could also be a (also async) function that returns a boolean.
username: /^[A-z]*$/,
}
```

###### Example: Function

```typescript
const validators = {
username: (value: string) => value.lenght > 3,
}
```

###### Example: With Object

```javascript
const validators = {
username: {
validator: /^[A-z]*$/,
message: 'My custom error message',
},
}
```

###### Example: Multiple Validators

```javascript
const validators = {
username: [
{
validator: /^[A-z]*$/,
message: 'My custom error message',
},
/[\d]/,
async (value) => value.length > 0,
{
validator: (value) => true,
message: 'Some other error',
},
],
}
```

###### Example: Dynamic Error Message

```javascript
const validators = {
username: async (s: string) => {
const taken = await API.isUsernameTaken(s)
return taken ? 'Username is taken' : true
},
}
```

### Options

Sometimes it's practical to have some different default values when using for example react-native or some other framework where the default `value`, `onChange` and `(e)=> e.target.value` do not apply.

###### Example: React Native (Method 1 - Global options)

[Check the Expo Snack for a live preview](https://snack.expo.io/@cupcakearmy/useform)

```javascript
import * as React from 'react'
import { Text, SafeAreaView, TextInput } from 'react-native'
import { useForm } from 'formhero'

const initial = {
username: 'i am all lowercase',
}
const validators = {}
const options = {
setter: 'value', // This is not stricly necessarry as 'value' would already be the default.
getter: 'onChangeText',
extractor: (text) => text.toLowerCase(),
}

export default () => {
const { form, field } = useForm(initial, validators, options)

return (


{form.username}

)
}
```

###### Example: React Native (Method 2 - Local overwrite)

```javascript
// ...

export default () => {
const { form, field } = useForm({
username: 'i am all lowercase',
})

return (

text.toLowerCase(),
})}
/>
{form.username}

)
}
```

### field

The `field` object is used to bind the form state to the input.

###### Example: Simple

```javascript
const { field } = useForm()

```

###### Example: With custom options

All are optional.

```javascript
const { field } = useForm()

e.target.value
})} />
```

## Form

This is the form state that you can use when submitting the data

###### Example

```javascript

const { form } = useForm(...);

// ...

console.log(form)}>
// ...

```

## Errors

This object contains the error messages if a field is not valid.
The error message can be specified by you, otherwise it will return `Error in ${field}`

###### Example

```javascript
const { errors } = useForm(...)

//...

{errors.username}
{errors.password}
```

## isValid

`isValid` is a little simple helper that checks whether the `error` object is clear or if there are errors left.

## setField

The `setField` function allows you to manually change and assign the state of a field. The type of the field must be the same as the initial type given in the constructor.

###### Example

```javascript
const { form, setField } = useForm(...)

const resetUsername = () => {
setField('username', 'new value')
}
```

## setForm

The `setForm` function allows you to manually change and assign the state of the form. This can be usefull when you want to reset a field or the whole form. The input must have the same type as the initial state.

###### Example

```javascript
const initial = {username: '', password: ''}

const { form, setForm } = useForm(initial, ...)

const resetForm = () => {
setForm(initial)
}
```

## setErrors

The `setErrors` function allows you to manually change and assign the state of the errors. This can be usefull when you want to set an error manually (e.g. sent from the server).

###### Example

```javascript

const { form, setErrors } = useForm(...)

const setError = () => {
setErrors({username: 'Already taken'})
}
```

### Thanks & Attributions

- Thanks for [brendanmckenzie](https://github.com/brendanmckenzie) for suggesting to change `auto` to `field`.