Back

Atomic Forms in React

I've mostly worked with Formik for most of my react form validations and the one thing that always feels out of place is the re-initialization of state based on the initial values.

Now, I understand why it needs to be a flag since you don't want it to be considered a dirty field unless touched. This works but it also causes a few rendering issues and as a human who can forget, you often end up writing stuff that causes a 100 re-renders.

I could've sat and thought out a solution for this but I never did cause I was busy trying to churn out libraries after libraries that no one needs (it's fun though, you should try it).

Either way, on Apr 25th , 2022 , Dai Shi decided to play with an idea and created jotai-form.

Initial Annoucement

Now, this looked interesting

What's interesting about this?

Well, that'd need me to explain why and where you should be using jotai so for now I'll just leave it at that. It's interesting!.

Being able to write atoms that store form state isn't that hard but then adding validations to it would need to add derived atoms and then handling async and sync validators separately would require work. Then a form group level validator would require even more work and you'd end up copying these derived atoms and validator utils to every project you use jotai in or you could use jotai-form.

The library is its initial development right now but it basically handles atomic form state for you.

To elaborate. Atomic state with it's validator isolated from other states. Don't have to worry about the form's state needing a re-init since it isn't abstracted from you and is just a state like you'd use with useState, but instead you use useAtom here.

But this would make the dirty field logic break!?

True, also why the utils from the library allow you to change the logic used for the dirty field calculation. You can modify it to match with your way of working with the atoms.

Done with the teasing? Show me an example!

Sure, since the docs aren't added yet, you can use this example as a quick start or use the repository's examples folder instead.

import { atomWithValidate } from 'jotai-form'
import { useAtom } from 'jotai'
import * as Yup from 'Yup'

// define an atom that needs to be validated
const nameAtom = atomWithValidate('', {
  validate: name => {
    if (name === 'Reaper') {
      // throw an error to say that the value is invalid
      throw new Error("Nah, invalid name, you can't be Reaper")
    }
    return name
  },
})

// you can also use form validation libraries if you wish to
// yes it supports async validations
// you can use a backend API for the validation for stuff
const nameYupAtom = atomWithValidate('', {
  validate: async name => {
    return await Yup.string().required().validate(name)
  },
})

const Form = () => {
  const [name, setName] = useAtom(nameAtom)
  return (
    <>
      <input value={name.value} onChange={e => setName(e.target.value)} />
      <span>{!name.isValid && `${name.error}`}</span>
      <button disabled={!name.isValid}> Save Name </button>
    </>
  )
}

This might look like a lot of boilerplate code but it's actually not. It's more separation of concerns and these are named as atoms.

I've been using the above library for TillWhen and it's what's responsible for the multiple timers running on the screen. Though you won't know that since I haven't published that version yet.

Anyhow being able to segregate form validation from the component's own render cycles makes it a lot more functional oriented and in my opinion that's good for not having to guess if your form is the reason for the re-renders.

I mean, the form might be the reason but that's very easier to check since the state is exposed to you and is not abstracted which makes it harder to debug as to what change is causing the re-render. Instead, you can just add a useEffect on the form state to see if it's the one causing the undefined behaviour.

Anyway, I like the library and also why I'm contributing to it. That's about it for the post.

Adios!