React State Management with Recoil
Tags: recoil, statemanagement, react, javascript
Published: 2021-01-07 10:52:52

---
title: React State Management with Recoil
published: true
tags: recoil, statemanagement, react, javascript
canonical_url: https://kinga.codes/react-state-management-with-recoil
cover_image: https://kinga.codes/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1665922254241%2Fh-0u_OMoq.png%3Fw%3D1600%26h%3D840%26fit%3Dcrop%26crop%3Dentropy%26auto%3Dcompress%2Cformat%26format%3Dwebp&w=3840&q=75
---

[Recoil](https://recoiljs.org) is a state management library for React. It is still in experimental phase, but it looks really promising. The best thing about Recoil is that it works and thinks like React. The most important concepts of Recoil are atoms and selectors.

Atoms are units of state, while selectors are pure functions that calculate derived data from state. Selectors accept both atoms and other selectors as input. Components can subscribe to selectors or atoms, and will be re-rendered when the selectors or atoms change.

![recoil](https://www.frontendlove.blog/static/0011242f3bac8a7037d3272a79957c20/0ae55/image-1.png "recoil")

<figcaption>Created with <a href="https://sketch.io/sketchpad" target="_blank">Sketchpad</a></figcaption>

I will explain how Recoil can manage your application’s state through some examples. No, it won’t be another todo app. Our app will show a list of songs, and for each song we can get some extra info. I will share the GitHub repository at the end of the article.

<img width="100%" style="width: 100%" src="https://media.giphy.com/media/VgSjnwSoqiPjRRIJ1F/giphy.gif" />

First of all, we need to create a new React app:

```bash
npx create-react-app recoil-example
cd recoil-example
yarn
yarn start
```

Check that your app works on [localhost:3000](http://localhost:3000), you should see a page like this:

![recoil](https://www.frontendlove.blog/static/e5bb392103593660285cc6df6010c1ea/3643c/image-2.png "recoil")

Then we need to add Recoil to our app:

```bash
yarn add recoil
```

We need to wrap our components that use Recoil in _RecoilRoot_. We can replace the content of _App.js_ with:

```jsx
// App.js

import React from 'react';
import { RecoilRoot } from 'recoil';

import './App.css';

const App = () => (
  <div className={'App'}>
    <RecoilRoot>
      <h1>Recoil Example</h1>
    </RecoilRoot>
  </div>
);

export default App;
```

Our app should still work and show the changes we made:

![recoil](https://www.frontendlove.blog/static/dde1f508d17e3a40ca4bf66b43acf490/3e86e/image-3.png "recoil")

We will create a real-world-like example, so we will start with our client:

```js
// client.js

const songList = [
  { id: 1, title: 'Bohemian Rhapsody' },
  { id: 2, title: 'Purple Rain' },
  { id: 3, title: 'One' },
  { id: 4, title: 'Eternal Flame' },
];

const songDetails = [
  { id: 1, artist: 'Queen', year: 1975 },
  { id: 2, artist: 'Prince', year: 1984 },
  { id: 3, artist: 'U2', year: 1992 },
  { id: 4, artist: 'The Bangles', year: 1989 },
];

export const getSongs = async () =>
  new Promise(resolve => setTimeout(() => resolve(songList), 500));

export const getSongById = async id =>
  new Promise(resolve => {
    const details = songDetails.find(s => s.id === id);
    return setTimeout(() => resolve(details), 500);
  });
```

Now that we have our client functions, we can implement the atoms and selectors that will manage our app’s state. Each atom and selector will have a unique id. We will start with loading the song list. As our client function returns a promise, the selector’s get function will be async:

```js
// selectors.js

import { selector } from 'recoil';
import { getSongs } from './client';

export const songsQuery = selector({
  key: 'songs',
  get: async () => {
    const response = await getSongs();
    return response;
  },
});
```

Next step is to create a component which renders the list of songs. We need to connect our component to the selector we just created. Recoil has some useful hooks for this:

- _useRecoilState_ — returns the value of the given state and the setter function for updating the value of given the state;
- _useRecoilValue_ — returns the value of the given state;
- _useSetRecoilState_ — returns the setter function for updating the value of given the state.

We will create the _Songs_ component:

```jsx
// Songs.js

import React from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { songsQuery } from './selectors';
import { currentSongIDState } from './atoms';

const Songs = () => {
  const songs = useRecoilValue(songsQuery);
  const setCurrentSongID = useSetRecoilState(currentSongIDState);

  return (
    <>
      <h2>Songs</h2>
      {songs.map(song => (
        <div key={song.id}>
          <p onClick={() => setCurrentSongID(song.id)}>{song.title}</p>
        </div>
      ))}
    </>
  );
};

export default Songs;
```

We should note that our selector is async, but React render functions are synchronous. Here comes in React Suspense, which handles pending data. We could also handle pending state with Recoil’s Loadable, or implement a handler from scratch, but we will use Suspense now:

```jsx
// App.js

import React, { Suspense } from 'react';
import { RecoilRoot } from 'recoil';
import Songs from './Songs';

import './App.css';

const App = () => (
  <div className={'App'}>
    <RecoilRoot>
      <Suspense fallback={<span>Loading...</span>}>
        <Songs />
      </Suspense>
    </RecoilRoot>
  </div>
);

export default App;
```

Now in our browser we should see the list of songs:

![recoil](https://www.frontendlove.blog/static/5e823d6d2ff2004c52bef668208bdec2/faa4a/image-4.png "recoil")

That was easy, right?

<img width="100%" style="width: 100%" src="https://media.giphy.com/media/FlWgXEtj5aM5G/giphy.gif" />

Now let’s see how can we get the details of a song. When we select a song, we want to see its details, like the artist and the year of release. We need to remember the current song ID. The ID is just a simple value, it will not be computed, so we will create an atom for this, instead of a selector:

```js
// atoms.js

import { atom } from 'recoil';

export const currentSongIDState = atom({
  key: 'currentSongID',
  default: '',
});
```

Based on the current song ID we want to get the song details. We need another selector which calls the client function with the current song ID. Selectors can read other atoms and selectors using the _get_ argument of the _get_ function. I know it sounds a little confusing, but the next example will make it more clear:

<img width="100%" style="width: 100%" src="https://media.giphy.com/media/XQvhpuryrPGnK/giphy.gif" />

```js
// selectors.js

import { selector } from 'recoil';
import { currentSongIDState } from './atoms';
import { getSongs, getSongById } from './client';

// ...

export const currentSongQuery = selector({
  key: 'currentSong',
  get: async ({ get }) => {
    const response = await getSongById(get(currentSongIDState));
    return response;
  },
});
```

We will now create the _CurrentSong_ component, which renders the details of the selected song:

```jsx
// CurrentSong.js

import React from 'react';
import { useRecoilValue } from 'recoil';
import { currentSongQuery } from './selectors';

const CurrentSong = () => {
  const currentSong = useRecoilValue(currentSongQuery);

  return currentSong ? (
    <>
      <h2>Current Song Details:</h2>
      <p>Artist: {currentSong.artist}</p>
      <p>Released: {currentSong.year}</p>
    </>
  ) : null;
};

export default CurrentSong;
```

Then we can add it to our _Songs_ component. The _currentSongIDState_ atom can be updated from the component by using the setter function returned by _useRecoilState_. (Note that I didn’t want to add it to the App component, because I don’t want to show the “Loading…” state when nothing is selected. Of course, we could structure our app better, but for now it’s just fine):

```jsx
// Songs.js

import React, { Suspense } from 'react';
import { useRecoilValue, useRecoilState } from 'recoil';
import { songsQuery } from './selectors';
import { currentSongIDState } from './atoms';
import CurrentSong from './CurrentSong';

const Songs = () => {
  const songs = useRecoilValue(songsQuery);
  const [currentSongID, setCurrentSongID] = useRecoilState(currentSongIDState);

  /*
   * as an alternative, we could declare them separately:
   * const currentSongID = useRecoilValue(currentSongIDState);
   * const setCurrentSongID = useSetRecoilState(currentSongIDState);
   */

  return (
    <>
      <h2>Songs</h2>
      {songs.map(song => (
        <div key={song.id}>
          <p onClick={() => setCurrentSongID(song.id)}>
            {song.title} {song.id === currentSongID && '*'}
          </p>
        </div>
      ))}
      {currentSongID && (
        <Suspense fallback={<span>Loading...</span>}>
          <CurrentSong />
        </Suspense>
      )}
    </>
  );
};

export default Songs;
```

If we click on a song we should see the details below the song list:

![recoil](https://www.frontendlove.blog/static/f243fc4becdc98d8e10fc6974b9d1278/3643c/image-5.png "recoil")

It was easy and fun so far, while working with read-only data, but in real-world apps we want our app’s state to get updated after doing an update on the server. For example, we might want to add new songs to our list. Here it becomes a little more complicated.

If you are used to work with other state management libraries, like Redux, then you know that the “global” state can be updated after updating the data on the server. Recoil does not have a “global” state, like other state management libraries, but coupled to _RecoilRoot_. That means the state can not be updated outside of the components/hooks.

<img width="100%" style="width: 100%" src="https://media.giphy.com/media/BEob5qwFkSJ7G/giphy.gif" />

But there is still hope... with Recoil we can achieve this by subscribing to server updates from _useEffect_, and updating the state from there. I know this is not ideal, but this [API](https://recoiljs.org/docs/guides/asynchronous-state-sync) is still under development, and Recoil might handle this out-of-the-box.

In conclusion, comparing it with other state management libraries (like Redux), it seems more “React-like” and simpler and easier to learn, so it might be a good alternative in the future.

You can find the GitHub repository [here](https://github.com/kingahnd/recoil-example). Thank you for reading this article.