Introduction
Atomic state management libraries are growing. Fast. As you can see in this graph from npmtrends, the two most famous libraries Recoil and Jotai were almost unknown last year, and now count respectively 240k and 35k downloads per week:
I've also open-sourced a new atomic library called Particule, which has some new interesting features such as custom atom
functions with hooks.
But what is atomic state management?
Ha, that's a good question. From Recoil's docs:
Atoms are units of state. They're updateable and subscribable: when an atom is updated, each subscribed component is re-rendered with the new value.
An atom represents a piece of state that you can read and update anywhere in your application. Think of it as a useState
that can be shared in any component.
This approach provides many benefits, mainly:
Avoid unwanted re-renders and computations
You won't anymore pass state values and update functions via props
, nor trigger the whole tree of components from a context's consumer. Only components subscribed to the atom's value will update.
Take a look at this example using Particule:
const textAtom = atom('Hello world!')
function Text() {
const text = useGetAtom(textAtom)
return <p>{text}</p>
}
// Won't re-render!
function Button() {
const setText = useSetAtom(textAtom)
return <button onClick={() => setText('Updated!')}>Update</button>
}
// Won't re-render!
export default function App() {
return (
<>
<Text />
<Button />
</>
)
Only <Text />
will re-render, even if <Button />
also uses the atom. But there's a difference between the two: <Button />
isn't subscribed to updates, because it uses useSetAtom
and only retrieves a setter function (similar to the one in useState
. On the contrary, <Text />
is subscribed to updates because it uses useGetAtom
to retrieve the value.
Compose atom from other atoms
Derived (or composed) atoms are atoms made from other atoms. They subscribe to each atom that they depend on, and such automatically trigger a re-render when needed.
This can be made using selectors in Recoil, and is even simpler in Jotai and Particule. This example is made with Particule:
const eurosAtom = atom(10)
const dollarsAtom = atom(get => get(eurosAtom) * 1.15)
function App() {
const [euros, setEuros] = useAtom(eurosAtom)
const [dollars, setDollars] = useAtom(dollarsAtom)
return (
<>
<input onChange={({ target }) => setEuros(target.value)} value={euros} />
<input onChange={({ target }) => setDollars(target.value)} value={dollars} />
</>
)
}
When eurosAtom
changes, dollarsAtom
is re-calculated since it is subscribed using get
and re-render the component once.
There are many other interesting topics with atomic state management libraries, such as <Suspense />
support for async, helper atom
functions to add new functionalities...
So feel free to comment down below if you want more articles on this, and you can check out the GitHub repository of the libraries I mentioned:
Top comments (3)
Could also recommend Effector library (effector.dev/), which is framework agnostic, but primarily used with react.
Recoil ftw
I would also like to recommend App's Digest, a simple, atomic state management library primarily for React.