How to Manage Your React Application State With Recoil.js, Part 1/2

La Javaness IT
6 min readJan 13, 2021

--

Nowadays, the first thing one does when they start developing a React application is to use Redux (or the React Context API). After all, we need to manage our application state.

Unfortunately, Redux is not without drawbacks:

  • The setup for Redux is long, especially when you just want to manage your state for a little / medium application
  • The learning curve can be steep: when you start to work with Redux, you have to understand concepts like actions, reducers, stores…

Neither is the Context API:

  • Whenever a context value changes, the whole tree of components under that context is re-rendered, which isn’t very efficient

In this article, we’ll present Recoil as an alternative state management library. We’ll show how to install Recoil and get started with it, and present an example using hooks and state management functions (atoms and selectors). The code used in the article is available at https://github.com/La-Javaness/recoil-pokedex/tree/local-state-part-1.

What is Recoil, And Why Should We Use It ?

Recoil is a state management library developed by Facebook and released at the React Europe 2020 summit.

The main purpose of Recoil is allow you to manage your global state easily and efficiently.

Easily, because the setup of Recoil boils down to wrapping your application with a provider, named <RecoilRoot />.

Efficiently, because contrarily to the React Context API, not every child component will re-render as the Recoil state changes.

At the time of publishing this article, Recoil is still in beta version (0.1.1). This release still has experimental for React Native and doesn’t yet have browser devtools (though they are under development). Until then, the API for observing, inspecting, and managing global Recoil states is described in this part of the official Recoil documentation.

Recoil Concepts Used in This Tutorial

To initialize Recoil on an application, there is a component wrapper named <RecoilRoot />. This is the state provider of your whole application. <RecoilRoot /> has an optional prop initializeState to create an initial state.

Recoil is based on the React Context API, but with improved performance: only components that have subscribed to the state will be re-rendered, instead of the whole component tree. As with React Contexts, you can nest multiple <RecoilRoot /> in your application. If you do so, the innermost root will completely mask any outer roots and only its state will be accessible to its children.

Recoil provides the following concepts:

Atom

An atom is an object which represents a piece of state. Atoms can be read and written to from any component. Components that read the value of an atom are implicitly subscribed to that atom, so any atom updates will result in them being re-rendered. An atom constructor accepts a unique key string and a default value, and returns a JS object.

Selector

A selector is an object which represents a piece of derived state. Derived state is a transformation of state. You can think of derived state as the output of passing state to a pure function that modifies it in some way. Selectors can be synchronous or asynchronous. A selector constructor accepts a unique key string, and get / set functions to derive a value from a selected atom and to update the atom’s value. The selector constructor returns a JS object that can be treated as equivalent to an atom.

Because the atom and selector concepts can often be used interchangeably, the Recoil documentation refers to Recoil state to mean either an atom or a selector.

Recoil Hooks

The useRecoilState hook implicitly subscribes the component calling it to a given Recoil state. This API is similar to the React useState() hook except it takes a Recoil atom (or selector) instead of a default value as an argument. The hook returns both the current value and a setter function.

The useRecoilValue returns the value of a given Recoil state. This hook also subscribes the callee component to updates of the used state. However, it only returns the current value of the atom, without the setter function.

The useSetRecoilState returns the setter function of a writeable Recoil state, without the current value! Unlike the two previous hooks, useSetRecoilState doesn’t subscribe the component to value changes. A recoil state is writeable if it is an atom or a selector with a set function.

Basic Usage

First of all, you need to install Recoil.js.

yarn add recoil

Once it’s done, you just need to wrap your application with the <RecoilRoot />.

Very easy! This is why Recoil is more suitable than other solutions like Redux for small / medium applications.

Whole-Application Example — A Simple Pokedex

The application we’ll build will have the following features:

  • List the pokemons you have
  • Add a pokemon
  • Filter your pokemons by type
  • Give statistics on your pokemon collection

All assets used in this example are available on GitHub. A quick note on writing conventions: to help recognise atoms and selectors in our code, each of them will be suffixed with “Atom” or “Selector”, eg. the atom initialising our list of pokemon is named pokemonListAtom.

With Recoil, everything begins with an atom, so we need one to create our list of pokemons. As you can see, these pokemons are French ;-)

With this atom, you have initialized a piece of state. Each component using this atom will subscribe to it. If you’ve opened the GitHub link, you’ve noticed that we created a folder dedicated to the management of our application state, the state folder, in which the following files are found:

  • atoms.js, which contains all the atoms we create
  • selectors.js, which contains all the selectors

Now we can create a basic component that displays our pokemon list. To retrieve the value of our atom, this component calls the useRecoilValue hook.

Next, we can build a selector. Let’s pretend we want to filter our pokemon list. We will create an atom that represents the current filter to apply, and a selector that applies this filter atom to the pokemonListAtom atom to produce a filtered list of pokemons. We’ll add the following to our state/atoms.js file:

And the following to our state/selectors.js file. The get function allows you to read and transform the value of one (or several) atom(s).

What if you want more information on your pokemon list? Let’s extract some statistics, based on the same atom and provide them as a new selector.

We can now create a Filter.js component :

If we wanted to add a pokemon, we would need to update the pokemon list atom. We can achieve this with useSetRecoilState, which allows us to update an atom’s value.

In the below code, we implement a form to add a pokemon. The form uses a normal React state to store its input values. The onSubmit callback adds the current pokemon state to the pokemon list get and resets the form.

The form is never re-rendered due to changes in the atom, as useSetRecoilState does not subscribe its caller to updates.

With all of the above, you can now add a new pokemon to your pokedex, filter it and produce statistics on pokemon elements.

How to Scale Your Application With Recoil?

If you want to use Recoil for a large application, you’ll probably need to fetch data from an API, the tools you need for that are included in Recoil, including:

  • Asynchronous selectors
  • Suspense
  • selectorFamily
  • atomFamily
  • useRecoilCallback

We’ll extend our pokedex using those concepts in a second article, so feel free to click the Follow button below if you don’t want to miss it! You can also hear more from us on Linkedin and Twitter and our website, check our GitHub, see our other publications on Medium.

I would like to thank Steve Dodier-Lazaro and Vincent Le Badezet for proofreading this article.

About

Maxime Conan is Lead Frontend Developer at La Javaness since 2019

--

--

La Javaness IT

La Javaness brings your team and your business to the AI ​​revolution!