Usetheform: React Library for Composing Declarative Forms

Antonio Pangallo
DailyJS
Published in
6 min readOct 22, 2020

Zero dependencies, it only uses the Context API and React Hooks.

usetheform

Introduction

A web form consists of a collection of HTML field elements grouped within a <form> tag. HTML supports different field elements, like for instance:

  • <input /> which defines an HTML form for user input
  • <textarea /> which defines a multi-line input control (text area)
  • <select /> which defines a drop-down list

For a complete list of all the HTML form elements supported, please refer to w3schools form elements.

Semantically, creating forms is easy. As long as the form’s complexity is low, you may not need a library to handle form submissions. However, the form’s complexity — in my experience — can increase quite easily. In most instances, we will at the minimum have to deal with use cases like:

  • Inputs validation, sync and async
  • Dynamically adding and removing fields
  • Form state management

React does not come with a prebuilt solution for overcoming those cases, but it provides a very useful technique called “controlled components” which may facilitate developers in addressing them.

If you wanted to know more about it, please refer to react controlled components.

Although this technique gives control over form’s input values, it does not accomplish aspects such as field’s validation (sync and async). Thinking on developing custom strategies within your projects to address them it might easily turn into a nightmare.

Luckily, out there, there are many React libraries which are valid solutions. For instance: Formik, ReduxForm, Final Form, React-Hook-Form and many others.

In today article we are going to talk about usetheform a react library for composing declarative forms and managing their state. Let’s start it.

Installation

To install the package run the following npm command:

npm i usetheform --save

NPM package: https://www.npmjs.com/package/usetheform

GitHub project: https://github.com/iusehooks/usetheform

Documentation: https://iusehooks.github.io/usetheform/

Basic concepts

Usetheform, as any other react library, provides basic components and exposes a declarative API for composing HTML forms. Let’s analyze some of its core components and then create our first basic form.

Core Components:

  • <Form /> renders all the fields and keeps the entire form state synchronized.
  • <Collection /> creates a nested piece of state within a Form and it can can be of type: object or array.
  • <Input /> renders all the inputs of type listed at: W3schools Input Types and accepts as props any HTML attribute listed at: HTML Input Attributes.
  • <Select /> creates a drop-down list and accepts as props any HTML attribute listed at: HTML Select Attributes.
  • <TextArea /> renders a textarea element W3schools Textarea and accepts as props any html attribute listed at: Html Textarea Attributes.

Our First Basic Form:

Basic Form Example

Well, we just created a very simple <Form /> with an initial state and the following props:

  • onSubmit : function invoked on Form submission.
  • onChange: function invoked when any of the Form’s fields change their value.
  • onInit: function invoked when the Form is initialized.

As result of that, once users press the submit button without changing any inputs value, we should expect to log into browser’s console the form state as following:

Output:

{ name: 'foo', color: 'BLUE', option1: 'option1' }

Now, let’s make the form more interesting by adding a bit of complexity. We want to have the following state logged into browser’s console after form submission:

Expected Output:

{ user: { name: 'foo', lastname: 'pluto' }, 
colors: ['BLUE', 'GREEN', 'RED'] }

Analyzing the expected output we can see an object named user within the form state which holds two props name, lastname and an array named colors with three items.

The <Collection /> component can easily get that job done. Let’s see how.

Collection Example

Simple? isn’t it? <Collection /> components in usetheform create a nested piece of state within a <Form />, they can be easily nested to compose more complex form states. For instance:

Nested Collections

In the above example the initial state was not passed as prop because usetheform provides two ways for initializing its fields (Collections, Inputs, Selects, TextAreas):

  1. By passing the initial value down using the initialState prop.
  2. By passing the initial value down using the value prop for any <Input />, <Collection /> , <Select />, <TextArea /> except for the <Input /> of type checkbox and radio where we have to pass a value prop but also indicate if we want that value to be set as default using the checked prop.

So at form submission the output will be:

Reducers

Reducers functions specify how the form’s state change. They can be applied to any component: <Form />, <Input />, <Collection /> , <Select />and<TextArea />.

Basic usage

The reducers prop passed to <Collection reducers={fullNameFN} /> can be either of type array or function.

(nextValue, prevValue, formState) => nextValue

When either the input field name or lastname change, the value of fullName is going to be the result of the concatenation: name + lastname.

Use cases and implementations might be numerous and I leave them to your imagination and your practical needs. The next topic I would like to analyze is one of the most important aspects of writing HTML forms: the form validation.

Form Validation

Forms validation is one of the cardinal point when you write HTML forms. Usetheform provides APIs for dealing with sync and async validations.

Basic usage: Sync Validation

Sync Validation

useValidation hook provides the sync validation logic for the fields <Input />, <Collection /> , <Select />and<TextArea /> .

const [status, validationAttr] = useValidation([...functions])

It takes as argument an array of functions which will be invoked following the array order.

In the above example another hook named useForm has been used to create a component named Sumbit which is a button enabled only if form passes all the validations functions added.

Basic usage: Async Validation

Async Validation

useAsyncValidation hook provides the async validation logic for the fields <Input />, <Collection /> , <Select />and<TextArea /> .

const [asyncStatus, asyncValidationAttr] = useAsyncValidation(fn);

It takes as argument a function which receives the value of the field and returns a promise. If the form contains at least one async validator function applied to any of its field, the value isValid of the useForm hook, which represent whether or not the form is valid, will be false until all the async validators are not resolved with success.

The asyncStatus is an object which holds two props: status and value .

The prop value is whatever value the promise returns.

The prop status can be one of the following:

  • undefined: async validation did not start yet
  • asyncStart: async validation has started
  • asyncSuccess: the promise has been resolved with success
  • asyncError: the promise has been rejected

Now that we saw how sync and async form validation can be achieved, before getting to the conclusions, I would like to show how fields can be either added or removed dynamically.

Addition/Removal: Fields or Collections

Dynamic addition or removal of form fields are often recurrent requirements that as developers we have to deal with. The following codesandbox implementation will show how it can be easily achieved using usetheform.

shopping cart

For further details about the API reference, please refer to the doc: https://iusehooks.github.io/usetheform/ where you can also learn how to use the following hooks:

  • useField: a hook which allows to build a custom input primitives.
  • useForm: a hook that returns helpers and the current state of the form.
  • useCollection: a hook which allows to build a custom Collection of type object or array.

Another live example you can play with:

Others CodeSandbox Examples

Conclusion

Usetheform could be another valid choice among many others react libraries out there. At the moment of this writing the version of the library is the 3.0.0 and the gzipped bundle size is 7.4kB.

A big thanks to marco sparagna for being the reviewer of this article

I hope you enjoyed reading the article. 👏 Claps are welcome!

--

--

Antonio Pangallo
DailyJS
Writer for

Software Developer 💻 - Code does not come from coder’s hands but from thinker’s minds