Created
June 10, 2023 22:26
-
-
Save scarf005/f9b7830c6630c81beb453538ab244114 to your computer and use it in GitHub Desktop.
unnecesarily complex useform with only string input
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const mapEntries = <T, U>( | |
obj: Record<string, T>, | |
fn: (entry: [string, T]) => [string, U], | |
) => Object.fromEntries(Object.entries(obj).map(fn)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ChangeEvent, FormEvent, useState } from "react" | |
import { | |
UseInputValidation, | |
useInputValidation, | |
useInputValidations, | |
ValidationState, | |
} from "./useInputValidation.ts" | |
import { mapEntries } from "./mapEntries.ts" | |
export type UseInput = { | |
value: string | |
handle: (e: ChangeEvent<HTMLInputElement>) => void | |
state: ValidationState | |
} | |
export const allValid = (props: UseInput[]) => | |
props.every(({ state: { type } }) => type === "valid") | |
export const useInput = () => { | |
const [value, set] = useState("") | |
const { state, validate } = useInputValidation() | |
const handle = (e: ChangeEvent<HTMLInputElement>) => { | |
validate(e) | |
set(e.target.value) | |
} | |
return { value, handle, state } | |
} | |
export const useForm = <const T extends readonly string[]>( | |
keys: T, | |
) => { | |
const validations = useInputValidations(keys) | |
const [vals, set] = useState<Record<string, string>>( | |
Object.fromEntries(keys.map((key) => [key, ""])), | |
) | |
const result = mapEntries(vals, ([key, val]) => { | |
// @ts-expect-error: to much work to zip with type safely | |
const { validate, state } = validations[key] as UseInputValidation | |
return [ | |
key, | |
{ | |
value: val, | |
state, | |
handle: (e: ChangeEvent<HTMLInputElement>) => { | |
validate(e) | |
set({ ...vals, [key]: e.target.value }) | |
}, | |
}, | |
] | |
}) | |
const values = mapEntries(vals, ([key, val]) => [key, val]) as { | |
[K in T[number]]: string | |
} | |
const allValid = Object.values(result).every( | |
({ state: { type } }) => type === "valid", | |
) | |
const handleSubmit = | |
(fn: (v: typeof values) => void) => (e: FormEvent<HTMLFormElement>) => { | |
e.preventDefault() | |
if (allValid) fn(values) | |
} | |
return { | |
inputs: result as { [K in T[number]]: UseInput }, | |
allValid, | |
values, | |
handleSubmit, | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ChangeEvent, useState } from "react" | |
import { mapEntries } from "./mapEntries.ts" | |
export type ValidationState = | |
| { type: "valid" } | |
| { type: "invalid"; message: string } | |
export type UseInputValidation = { | |
state: ValidationState | |
validate: (e: ChangeEvent<HTMLInputElement>) => void | |
} | |
const defaultValidity = { | |
type: "invalid", | |
message: "", | |
} satisfies ValidationState | |
export const getValidity = ( | |
e: ChangeEvent<HTMLInputElement>, | |
): ValidationState => | |
e.target.checkValidity() | |
? { type: "valid" } | |
: { type: "invalid", message: e.target.validationMessage } | |
export const useInputValidation = () => { | |
const [val, set] = useState<ValidationState>(defaultValidity) | |
const validate = (e: ChangeEvent<HTMLInputElement>) => set(getValidity(e)) | |
return { state: val, validate } | |
} | |
export const useInputValidations = <const T extends readonly string[]>( | |
keys: T, | |
) => { | |
const [vals, set] = useState<Record<string, ValidationState>>( | |
Object.fromEntries(keys.map((key) => [key, defaultValidity])), | |
) | |
const result = mapEntries(vals, ([key, val]) => [ | |
key, | |
{ | |
state: val, | |
validate: (e: ChangeEvent<HTMLInputElement>) => | |
set({ ...vals, [key]: getValidity(e) }), | |
}, | |
]) | |
return result as { [K in T[number]]: UseInputValidation } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment