DEV Community

OGURA_Daiki
OGURA_Daiki

Posted on

4 1

react-use-form: Type safe React Hooks based form utility

Managing forms in React is hard but boring task.
In addition, you are going to suffer from type unsafeness due to of native DOM input restrictions if you preferred TypeScript.

There are several libraries to solve the problem. react-form, redux-form, formik...
Now I added the new one which names Mayoiga.

https://github.com/hachibeeDI/mayoiga

The charasteristics of Mayoiga are below:

  • React Hooks base (useForm to summon form component)
  • Strict type check (I love TypeScript!)
  • Simple API for less learning cost

How to use

This is minimum working example.

import * as React from 'react';
import {FC} from 'react';

import {useForm, createFormScope} from 'mayoiga';
import {Input, NumberInput} from 'mayoiga/lib/forms';

const required = (target: string) => (target.length === 0 ? 'required' : undefined);

const between = (min: number, max: number) => (target: number) => {
  if (target < min) {
    return `more than ${min}`;
  }
  if (target > max) {
    return `less than ${max}`;
  }
};

const choice = function<T>(...candidates: Array<T>) {
  return (target: T) => (candidates.includes(target) ? undefined : 'You should choose from the candidates.');
};

const INITIAL_FORM_STATE = {
  name: '',
  age: 13,
  race: 'fish',
};

const {context, scope} = createFormScope<typeof INITIAL_FORM_STATE>();

const DemoForm = scope(props => {
  const {Form, Field} = useForm(context);
  return (
    <Form onSubmit={value => console.log(value)}>
      <Field name="name" component={Input} validations={[required]} />
      <Field name="age" component={NumberInput} validations={[between(5, 20)]} />
      <Field name="race" component={Input} validations={[choice('fish', 'squid', 'octopus')]} />

      <button disabled={!props.touched || Object.values(props.errors).some(e => !!e.length)}>submit</button>
    </Form>
  );
});

export default function DemoApp() {
  return <DemoForm initialState={INITIAL_FORM_STATE} onSubmit={value => alert(`submit ${JSON.stringify(value)}`)} />;
}

Let me explain some lines that is essential. (These are still working in improve progress so may be changed)

Scope

const {context, scope} = createFormScope<typeof INITIAL_FORM_STATE>();

createFormScope is to create a scope which allows share the form state between Form and Fields.
A component was included in scope is going to have two additional props initialState and onSubmit. Those values are working you can see <DemoApp />.

Form

In form layer component, you can also handling onSubmit on <Form /> component in case you need.

<Field /> requires name and component, optionally validations.

name of the Field isn't just typed as string but keyof State. So that if you had typo like as <Field name="rage" ... TypeScript compiler warn like "rage is not a type of 'keyof typeof INITIAL_FORM_STATE'".

component is also strictly typed. A component should follow the signature named InputProtocol<State, Name> which is defined as below:

export type InputProtocol<S, Name extends keyof S, DelegatedProps> = Omit<InputHTMLAttributes<any>, 'name' | 'onChange' | 'value'> & {
  name: Name;
  value: S[Name];
  errors: ReadonlyArray<string>;
  touched: boolean;
  onChange(name: Name, value: S[Name]): void;
};

I have written some glue for native DOM component. You can import those from mayoiga/lib/forms.

You can also write glue for any components even if the component is complex which returns not primitive value i.e. Array or Object.

Validation

validations of <Field /> is a array of Validator. Definition of Validator is below.

type Validator<S, Name extends keyof S> = (target: S[Name], record: S) => undefined | string;

Validator can get the value of the target and also all other records. You can return string as error message.

Lastly

Mayoiga is under development and no documentation so far (because of my English skill XD) though any questions/issues/requests are welcome. :)

However, I think anyone who hate managing week typed input with boilerplate codes are interested in the concept.

Thanks!

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (1)

Collapse
 
hachibeedi profile image
OGURA_Daiki

Documentation is now published! hachibeedi.github.io/mayoiga/index...

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay