Skip to content

Instantly share code, notes, and snippets.

@garth
Created November 23, 2018 13:46
Show Gist options
  • Save garth/1f3f9e3c734a4d3a35a2e3fa200d17a3 to your computer and use it in GitHub Desktop.
Save garth/1f3f9e3c734a4d3a35a2e3fa200d17a3 to your computer and use it in GitHub Desktop.
A simple form manager with react hooks
import { useState } from 'react'
export interface FormField {
value: string
isPristine: boolean
isValid: boolean
}
export type IsValid = (value: string, form?: FormDefinition) => boolean
export interface FormDefinition {
[fieldName: string]: {
value?: string
isValid?: IsValid
}
}
export type FormFields<T extends FormDefinition> = { [F in keyof T]: FormField }
export type Form<T extends FormDefinition> = { isValid: boolean } & FormFields<T>
export type SetField = (name: string, value: string) => void
export type Reset = () => void
const validateForm = <T extends FormDefinition>(formDefinition: T): Form<T> =>
Object.keys(formDefinition).reduce<Form<T>>(
(form, fieldName) => {
const field = formDefinition[fieldName]
const value = field.value || ''
const isValid = typeof field.isValid === 'function' ? field.isValid(value, formDefinition) : true
form[fieldName] = {
value,
isPristine: field['isPristine'] === undefined ? true : field['isPristine'],
isValid
}
form.isValid = form.isValid && isValid
return form
},
{ isValid: true } as any
)
export const useForm = <T extends FormDefinition>(formDefinition: T): [Form<T>, SetField, Reset] => {
const [originalForm] = useState(formDefinition)
const [form, setForm] = useState(formDefinition)
const setField = (name, value) => {
setForm({
...(form as any),
[name]: {
...form[name],
value,
isPristine: false
}
})
}
const reset = () => {
setForm(originalForm)
}
return [validateForm(form), setField, reset]
}
import React from 'react'
import { useForm } from './forms-hook'
const formDefinition = {
username: { value: '', isValid: value => value.length > 0 },
password: { value: '', isValid: value => value.length > 0 }
}
const Login: React.StatelessComponent = () => {
const [form, setField] = useForm(formDefinition)
return (
<form
onSubmit={e => {
e.preventDefault()
console.log(form)
}}>
<h1>Sign In</h1>
<label>Username</label>
<input
value={form.username.value}
className={form.username.isPristine || form.username.isValid ? "" : "error"}
onChange={e => setField('username', e.target.value)}
/>
<label>Password</label>
<input
type="password"
value={form.password.value}
className={form.password.isPristine || form.password.isValid ? "" : "error"}
onChange={e => setField('password', e.target.value)}
/>
<button type="submit" disabled={!form.isValid}>
Sign In
</button>
</form>
)
}
export default Login
@garth
Copy link
Author

garth commented Nov 29, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment