Part 1: simplr-forms — declarative forms for React. Why are we doing this?

This is series of posts documenting the development of @simplr/react-forms library.

Originally posted on April 8th.

Why do we need yet another forms library?

Over the past few years we worked with React, we tried MANY form libraries that are out there.

Problems with existing libraries

  • There is no de-facto standard library for forms in React, like there is for routing (react-router).
  • They all have their limitations and usually big ones.
  • Performance is usually bottlenecking as the project grows.
  • No really flux friendly.
  • Not friendly with TypeScript.
  • There is not a single library that is built in the React way.

Ok, so the big one is the libraries can never be consumed in a React way, which raises a whole bunch of other problems:

  • You have to learn everything from the ground up to use the library.
  • When you write your app in a declarative react way, you have to fallback to imperative or semi-imperative way for forms.
  • Library has to reinvent the wheel to do things.
  • Lifecycle management becomes hell on earth.
  • Race conditions appear out of the blue.

I could go on here, but Michael Jackson said it better here and the problems are more than apparent.

So why do we start now?

Well, technically, we started almost a year ago (Jan 12, 2016) by building a simplr-forms v0.0.4 and used it internally.

v1.0.0 was also released internally on May 30, 2016.

v2.0.0 as well, released internally on September 7th, 2016.

We introduced v3.0.0-beta in Dec 7, 2016. And we made all possible mistakes during these 3 versions (hopefully).

We had 113 different alphas, betas, RCs and stable ones in between.

And more than a year later, we still don’t see a proper library for forms out there. This is why we are putting the efforts into spec’ing, architecting and building the library out there in public.

What’s the plan?

For a few days already we were talking and ploting the architecture, raising and solving problems on the whiteboard. At the moment we are ready to to start coding, but first — we’ll try to write out ideas.

Integrity with flux

During 3 versions, we tried different approaches, to have forms:

  • Live on their own
  • Live in the flux lifecycle
  • Something in between the first two

At the moment, we think the best way to have responsive forms (as of fast response time), we have to make the library live on its own.

And for architectural integration (flux/redux/mobx/etc), we hope to make initial flux and redux versions ourselves and then get some help from community as the library becomes the best forms library out there.

Let’s get to the point

Lifecycle

For forms lifecycle, we will be using somewhat flux-ish architecture of actions and at first and it’s gonna be quick-and-dirty switches and immutable objects. Later on, we will incorporate simplr-flux package, as we will open-source it soon.

Principles

  1. The source code is written in TypeScript.
  2. Architecture is compile-time checked as much as possible.
  3. Declarative API first. An imperative API is not going to be first-class citizen.
  4. Simplicity in developer-facing API, performance in the backend.
  5. Have React Fiber in mind.
  6. Validation is first-class citizen.

Code samples

To get the idea how JSX looks:

<Form onSubmit={this.onFormSubmit}>
<Text name="FirstName">
<Validators.MinLength value={3} errorMessage="First name has to be at least ${value} long."/>
<Validators.MaxLength value={10} errorMessage="First name has to be at most ${value} long."/>
</Text>
<Submit>Submit</Submit>
</Form>

And the rendered DOM:

<form>
<input type="text" name="FirstName" />
<button type="submit">Submit</button>
</form>

As you can see, no wrappers, no custom elements, divs, spans or anything, just a clean form with inputs and buttons.

Having clean output really helps with styling, because you see the same structure in JSX, except for simplr-forms specific validators inside text element.

For now, we have to render validators in the virtual DOM, because inputelements cannot have childrens (at least some of them).

That will change with React Fiber coming in as it will have the ability to return array of elements in the render function. With that change, we will be able to render validators alongside, but, of course, that is yet to come.

Validators

A really important functionality while using forms is data validation.

And the main pains behind validation are:

  • Reusability of validators code
  • An easy way of defining the requirements of your data

Imperative API is probably the worst solution for this, which leaves a nice spot for declarative one. Unfortunately, there is no way of doing that today, but…

We have another package simplr-validation, which was released internally on Feb 15, 2016 and did not change much since. We had 19 versions of the package and most of them were adding functionality, not changing the core principles. Which means the package is done right from the start.

More interesting use case

Want to have your mind blown?

Suppose you need to validate your username to be:

  • 4 to 128 symbols of length
  • Unique in your system, which is a call to your server

Take a second to think how would you approach it today. Then look at this:

import { MinLength, MaxLength, DebouncerValidator } from "simplr-forms-core/validators";
<Text name="username">
<MinLength value={4} errorMessage="Min length is ${value}!" />
<MaxLength value={128} errorMessage="Max length is ${value}!" />
/*
Waits for 3 seconds before executing to the next validator.
If the value changes before debouncer expires,
the validation will be stopped and rejected as an error.
Also, form store will ignore validation changes until the value is the newest,
therefore store will update validation result to Valid or Invalid
only after ALL needed validators are executed.
*/
<DebouncerValidator value={3000} />
<CostyValidatorToTheServer />
</Text>

What you see here is a declarative way to check all of the requirements AND do it in an optimal manner, which is debouncing for some time until the user stops typing the username.

Mind blown? 🎉 I hoped so. 👏

Next in series

Part 2: Core, validation, testing.