Using Recoil instead of Redux For State Management In React Applications
Tags: redux, react, tutorial
Published: 2021-09-16 18:50:42

_by author [Origho Precious](https://blog.openreplay.com/authors/origho-precious)_

Among all the state management libraries available for use in React apps, [Redux](https://redux.js.org/) is the most popular even ahead of React’s [Context APIs](https://reactjs.org/docs/context.html). There are also other awesome state management libraries used in React apps one of which is [Recoil](https://recoiljs.org/). **Recoil** unlike Redux is very easy to set up, even easier to set up than the new [Redux toolkit](https://redux-toolkit.js.org/) library. 
In this article, we will learn how to use Recoil to manage states in React apps instead of Redux.


# What is Recoil?

According to [documentation](https://recoiljs.org/), 

> Recoil is a state management library for React applications. 

**Recoil** is an open-source state management library with more than 14k stars on [Github](https://github.com/facebookexperimental/Recoil), it was invented by Dave McCabe, a Software Engineer at Facebook. It provides a global state so all components in a React application can share states easily and it is minimal compared to Redux with no boilerplate code setup needed.

Recoil provides a **data-graph** that flows from shared states into React components. The two core concepts of Recoil according to the official [documentation](https://recoiljs.org/docs/introduction/core-concepts) are:

1. [**Atoms**](https://recoiljs.org/docs/introduction/core-concepts#atoms), which are units of the global state provided by Recoil, components can access and subscribe to changes made to them. 
2. [**Selectors**](https://recoiljs.org/docs/introduction/core-concepts#selectors) with which we can transform states either synchronously or asynchronously, and components can also access and subscribe to.


## Why Use Recoil?

Considering that we have many other state management libraries out there and also React’s Context API and component state, why then should we use Recoil in our React apps?. Let’s highlight some of the reasons below:

- Recoil just like Redux provides a **global state**. With Recoil, we won’t have to pass states as props down to children components in order to share them between components (a concept known as **prop drilling**).
- Once we hook up a component to any **Atom** or **Selector,** they are subscribed to it, so any update made to that piece of state will be reflected across the app to wherever it’s being used.
- With Recoil **Selectors,** we can transform a state synchronously or asynchronously and use the derived state anywhere in our app.
- Recoil is **minimal** and requires no boilerplate code to get started. Redux is very popular but many developers still frown at it because of the amount of code they need to write to set it up.


# Building a React App with Recoil

We have now gone over what Recoil is, its core concepts, and why you should consider using it. 
In this section, we will build an `anime-quote-generator`, this app will fetch quotes from an external [API](https://animechan.vercel.app) based on selected animes. 

Let’s get started by generating a new React app with the command below.


```
npx create-react-app anime-quote-generator
```

After that, open it in your favorite code editor.

Next, we will install Recoil and get started building our app components. Run the command below to install Recoil.


```
yarn add recoil
```

OR 


```
npm install recoil
```

Let’s configure our app to use Recoil. Navigate to `src/index.js`, here, we only need to wrap our entire app with `RecoilRoot`; a Recoil component. Let’s do that below.


```javascript
import React from "react";
import ReactDOM from "react-dom";
import { RecoilRoot } from "recoil";
import "./index.css";
import App from "./App";

ReactDOM.render(
  <RecoilRoot>
    <App />
  </RecoilRoot>,
  document.getElementById("root")
);
```
    

We have successfully set up Recoil in our React app just by wrapping all our app’s components with it. You see how easy the setup is, to do this using Redux we will have to write some lines of code to create a store before wrapping our app with the React-Redux Provider component that will contain the store. 

Now that we have set up Recoil in our app, let’s start building components, pages, and sharing states with Recoil.

## Building AnimePill Component

This component will render the title of an anime passed to it, and when clicked, it will route us to a page where we’ll see quotes from that anime. First, we need to install [react-router-dom](https://reactrouter.com/web/guides/quick-start) for routing between pages in our app and [styled-components](https://styled-components.com/) for styling. Let’s do that with the command below:


```
yarn add react-router-dom styled-components
```

OR


```
npm install react-router-dom styled-components
```

Next, let’s create a folder in the `src` folder, called `components`. In the `components` folder, create a folder called `AnimePills` and a file called `AnimePills.jsx` inside that folder. The path to this file from `src` should be `src/components/AnimePills/AnimePills.jsx`, now add the code below to that file.

```javascript
import { Link } from "react-router-dom";
import styled from "styled-components";

const AnimePill = ({ anime, color }) => {
  return (
    <StyledPill style={{ background: color }}>
      <Link to={`/anime/${anime}`}>{anime}</Link>
    </StyledPill>
  );
};

const StyledPill = styled.div`
  border-radius: 999px;
  & a {
    display: block;
    text-decoration: none;
    color: #333;
    padding: 1rem 2rem;
  }
`;

export default AnimePill;
```
    

Above, we just created a component called `AnimePills`. This component takes in 2 props; `anime` and `color`, with the anime we will construct a link using the `Link` component from `react-router-dom` and we will use the color as a background-color. We then style the component with `styled-components`.



## Building Quote and SmallQuote components

We will be building 2 components in this section. Let’s start with the `Quote` component. Inside our `components` folder, create a new folder called `Quote` and a file `Quote.jsx` inside it. In this component, we will simply render a quote from Naruto and style the component with `styled-components`. Add the code below to the file.


```javascript
import styled from "styled-components";

const Quote = () => {
  const quote = {
    anime: "Naruto",
    character: "Pain",
    quote:
      "Because of the existence of love - sacrifice is born. As well as hate. Then one comprehends... one knows PAIN.",
  };

  return (
    <StyledQuote>
      <p>"{quote.quote}"</p>
      <h4>
        <span className="character">{quote.character}</span> <em>in</em>{" "}
        <span className="anime">{quote.anime}</span>
      </h4>
    </StyledQuote>
  );
};

const StyledQuote = styled.div`
  background: #dbece5;
  padding: 3rem 5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  & > p {
    font-size: 2rem;
    letter-spacing: 2px;
    text-align: center;
    font-style: italic;
    margin-bottom: 3rem;
    background: #fff;
    border-radius: 0.5rem;
    padding: 3rem;
  }
  & > h4 {
    font-size: 1.5rem;
    font-weight: 500;
    letter-spacing: 2px;
    span {
      padding: 5px 10px;
    }
    em {
      font-size: 1.2rem;
    }
    & > .character {
      background: #b8dace;
    }
    & > .anime {
      background: #f5e7e4;
    }
  }
`;

export default Quote;
```
    

Next, let’s create the `SmallQuote` component. This component expects 3 props (anime, character and quote), we will render these props, and style the component with `styled-components`. To do this, create a folder inside `src/components` called `SmallQuote`, inside it create a file `SmallQuote.jsx` and add the code below


```javascript
import styled from "styled-components";

const SmallQuote = ({ quote, character, anime }) => {
  return (
    <StyledQuote>
      <p>"{quote}"</p>
      <h4>
        <span className="character">{character}</span> <em>in</em>
        <span className="anime">{anime}</span>
      </h4>
    </StyledQuote>
  );
};

const StyledQuote = styled.div`
  background: #dbece5;
  padding: 1.5rem 2.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  & > p {
    font-size: 1rem;
    letter-spacing: 2px;
    text-align: center;
    font-style: italic;
    background: #fff;
    border-radius: 0.5rem;
    padding: 1.5rem;
    margin-bottom: 1.5rem;
  }
  & > h4 {
    font-size: 1rem;
    font-weight: 500;
    letter-spacing: 2px;
    span {
      padding: 3px 5px;
    }
    em {
      font-size: 1rem;
    }
    & > .character {
      background: #b8dace;
    }
    & > .anime {
      background: #f5e7e4;
    }
  }
`;

export default SmallQuote;
```
    

Above, we just built a component called `SmallQuote` and styled it with `styled-components`. It is very similar to `Quote` component, the purpose of splitting them is to make the code easier to understand. So if you want to make the `Quote` component reusable to include features of the `SmallQuote`  component, feel free to do that.

Next, we will be building our app **atoms** and **selector.**


## Building Our App Global State (Atoms and Selector).

To get started, navigate into our `src` folder and create a folder called `store`, inside this folder create a file called `index.js`. In this file, we will build all the atoms we need and also a selector to modify one of the atoms. Let’s start with the first atom, `animeTitles`. Add the code below to create our first atom.


```javascript
import { atom } from "recoil";

export const animeTitles = atom({
  key: "animeTitleList",
  default: [],
});
```


Above, we created an Atom by importing `atom` from recoil and called it `animeTitles`. We then defined the required properties of an atom, which are

- `key` - this should be a unique ID among other atoms and selectors we will create in the app.
- `default` - this is the default value of the atom.

Doing this with Redux, we will have to create an [action creator](https://redux.js.org/tutorials/fundamentals/part-7-standard-patterns#action-creators) with a specific type, the action creator will return the type and a payload passed to it, and also we will create a [reducer](https://redux.js.org/understanding/thinking-in-redux/glossary#reducer) to update our redux store. but with Recoil we don’t need to handle all of that, with the `key` prop, Recoil knows the part of the global state to update so when we pass data to update the state it does it correctly and we won’t need to compare action types as we would do in Redux.

Following the same pattern, let’s create another atom called `animePageNum`. we will use this atom to hold page number, this will help us properly handle pagination in this app. Add the code below to this file.


    
```javascript
export const animeListPageNum = atom({
  key: "animeListPageNum",
  default: 0,
});
```
    

Next let’s create a selector to mutate the array of data we will have in the `animeTitles` atom by slicing the array to return only 50 items at a time based on the page number which we will get from `animePageNum` atom. 


```javascript
export const slicedAnimeTitles = selector({
  key: "slicedAnimeTitles",
  get: ({ get }) => {
    const animes = get(animeTitles);
    const pageNum = get(animeListPageNum);

    const newAnimeList = [...animes];
    const arrIndex = pageNum === 0 ? 0 : pageNum * 50 + 1;

    return newAnimeList.splice(arrIndex, 50);
  },
}); 
```


Above, we created a selector called `slicedAnimeTitles`, we defined a `key` property just like we did in the atoms we created above, and here we have a new property `get` whose value is a function, this is only available in selectors, this function has 2 parameters but here we are using just one of them that is `get` with which we can access the value of an atom or selector. Inside this function, with the `get` method, we saved the `animeTitles` and `animeListPageNum` atoms into 2 variables `animes` and `pageNum` respectively, and with the `pageNum` we specified the index to start slicing from and then returned a new array of just 50 items.

We have now successfully created all the shared states we will be using in this app. Next. Let’s create a pagination component, to handle user click and update the animeListPageNum state (atom) so we can update the list of animes we are returning from the selector we just created.



## Building Pagination Component

To begin, navigate to `src/components` and create a new folder `Pagination`, inside it create a file `Pagination.jsx`, paste the code below into this file.


```javascript
import { useState, useEffect } from "react";
import { useRecoilState } from "recoil";
import styled from "styled-components";
import { animeListPageNum } from "../../store";

const Pagination = ({ listLength }) => {
  const [pageNum, setPageNum] = useRecoilState(animeListPageNum);
  const [numsArr, setNumsArr] = useState([]);
```
    

Above, we created a new component `Pagination`. This component has a prop `listLength`; this will help us determine the page numbers to render. We then imported `[useRecoilState](https://recoiljs.org/docs/api-reference/core/useRecoilState)`, which accepts a state as an argument just like React’s useState hook, This also works similar to `useSelector`  in Redux But here we can update the state directly and not have to dispatch an action. We can access the value of the state passed to `useRecoilState` and also update the state, We also created a component state with `useState` hook to hold an array of numbers, these will be the page numbers to render in this component.


```javascript
useEffect(() => {
    const paginationNums = () => {
      const max = Math.floor(listLength / 50);
      let nums = [];
      for (let i = 0; i <= max; i++) {
        nums.push(max - i);
      }
      setNumsArr(
        nums.sort((a, b) => {
          return a - b;
        })
      );
    };
    paginationNums();
  }, [listLength]);

  return (
    <StyledPagination>
      {numsArr?.length
        ? numsArr?.map((num) => (
            <button
              className={pageNum === num ? "active" : ""}
              onClick={() => setPageNum(num)}
              key={num}
            >
              {num + 1}
            </button>
          ))
        : null}
    </StyledPagination>
  );
};
```
    

In the useEffect we imported above, we created an array of numbers with the `listLength` prop, and updated the `numArr` state with the array we created and then we looped through the array of nums and rendered them in buttons, each button will update the `animeListPageNum` when clicked. Let’s complete this component by adding  the code below.


```javascript
const StyledPagination = styled.div`
  display: flex;
  align-items: center;
  border-width: 2px 2px 2px 0;
  border-style: solid;
  width: max-content;
  & button {
    outline: none;
    background: transparent;
    border: none;
    border-left: 2px solid;
    width: 35px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    &:hover,
    &.active {
      background: #fae1da;
    }
  }
`;

export default Pagination;
```

With the `Pagination` component done, we can now build our app pages and complete the app.


## Building Homepage Component

In this section, we will build our `homepage` component, this page will render a static quote and also a list of animes using the `AnimePill` component we created earlier and also the `Pagination` component for pagination. To do this, in our `src` folder let’s create a folder called `pages`, in this folder create a folder called `home` and a file inside it called `index.jsx`, folder path should `src/pages/home/index.jsx`. Add the code below to this file


```javascript
import { useRecoilValue } from "recoil";
import styled from "styled-components";
import AnimePill from "../../components/AnimePill/AnimePill";
import Pagination from "../../components/Pagination/Pagination";
import Quote from "../../components/Quote/Quote";
import { slicedAnimeTitles, animeTitles } from "../../store";

const Homepage = () => {
  const animes = useRecoilValue(animeTitles);
  const slicedAnimes = useRecoilValue(slicedAnimeTitles);
  const colors = ["#FAE1DA", "#E8C6AD", "#F2E2ED", "#D6EBE4", "#BFDCD0"];

  const generateColor = () => {
    const randNum = Math.floor(Math.random() * 5);
    return colors[randNum];
  };

  return (
    <StyledHomePage>
      <header>
        <h2>Anime Quote Generator</h2>
      </header>
      <main>
        <Quote />
        <div className="animes">
          <h3>All Animes</h3>
          {animes?.length ? (
            <p>Click on any anime to see a quote from it</p>
          ) : null}
          <div className="flex">
            {animes?.length ? (
              slicedAnimes?.map((anime) => (
                <div key={anime}  style={{ margin: "0 1.3rem 1.3rem 0" }}>
                  <AnimePill anime={anime} color={generateColor()} />
                </div>
              ))
            ) : (
              <p className="nodata">No anime found 😞 </p>
            )}
          </div>
          {animes?.length > 50 ? (
            <div className="pagination">
              <Pagination listLength={animes?.length} />
            </div>
          ) : null}
        </div>
      </main>
    </StyledHomePage>
  );
};

const StyledHomePage = styled.div`
  max-width: 80%;
  margin: 2rem auto;
  & header {
    margin-bottom: 3rem;
    & > h2 {
      font-weight: 400;
      letter-spacing: 3px;
      text-align: center;
    }
  }
  & .animes {
    margin-top: 4rem;
    & > h3 {
      font-weight: 400;
      font-size: 1.4rem;
      background: #ece4f1;
      width: max-content;
      padding: 0.3rem 1rem;
    }
    & > p {
      margin: 1.2rem 0;
    }
    & > .flex {
      display: flex;
      justify-content: center;
      flex-wrap: wrap;
      & > .nodata {
        margin: 2rem 0 4rem;
        font-size: 1.3rem;
      }
    }
    & .pagination {
      display: flex;
      flex-direction: column;
      align-items: center;
      margin: 2rem 0 4rem;
    }
  }
`;

export default Homepage;
```

Above, we imported: 

- `useRecoilValue` from the Recoil library to get the state values,
- `styled` from `styled-components` to style the `Homepage` component, 
- `AnimePill` to render anime title, 
- `Pagination` to handle pagination, 
- `Quote`  to display a static anime quote,
- `SlicedAnimeTitles` is the selector we created earlier. We will be its return value on this page, and 
- `animeTitles` which is the first atom we created to hold the list of animes. 

Next, we created a function component called `Homepage` inside this component, we accessed the `animeTitles` and the `slicedAnimeTitles` state using `useRecoilvalue` and also we created an array of colors (we will pass these colors to the `AnimePill` component at random). We then created a function `generateColor`, this component returns a random color from the colors array. After that, we returned the component body styled with `styled-components`, a header, the `Quote`  component, and a little notice telling a user what to do, then if we have `animes`, we will loop through the `slicedAnimes` and render each of them with the `AnimePill` component by passing the anime to the component and also a color prop from the `generateColor` function and if there’s none we render a ‘no data’ state.  
Next, we are checking to see if the length of the `animes` state is more than 50, if true we render the `Pagination` component. and finally, we added a block of `styled-component` styles

We’ve now successfully created our `Homepage` component, in the next section let’s create a page we will be routed to when we click on any `AnimePill`. In that component, we will make an API call to the external API and fetch all quotes from the selected anime and render the quotes.



## Building Animepage Component

Let’s get started by navigating to our `pages` folder, inside create a folder called `anime` and a file inside it called `index.jsx`. Add the code below to the file.


```javascript
import { useState, useEffect } from "react";
import { Link, useParams } from "react-router-dom";
import axios from "axios";
import styled from "styled-components";
import SmallQuote from "../../components/SmallQuote/SmallQuote";

const Animepage = () => {
  const param = useParams();
  const [quotes, setQuotes] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    if (param?.name) {
      setLoading(true);
      const fetchAnimeQuotes = async () => {
        try {
          const res = await axios.get(
            `https://animechan.vercel.app/api/quotes/anime?title=${param?.name}`
          );
          setQuotes(res?.data);
          setLoading(false);
        } catch (error) {
          console.log(error);
          setLoading(false);
        }
      };
      fetchAnimeQuotes();
    }
  }, [param]);

  return (
    <StyledAnimePage>
      <h2>Quotes from {param?.name}</h2>
      <Link to="/">Go back</Link>
      <div className="grid">
        {loading ? (
          <p>Loading...</p>
        ) : quotes?.length ? (
          quotes?.map((quote, index) => (
            <div key={quote?.quote + index} className="anime">
              <SmallQuote
                anime={quote?.anime}
                character={quote?.character}
                quote={quote?.quote}
              />
            </div>
          ))
        ) : (
          <p className="nodata">No Quote found 😞</p>
        )}
      </div>
    </StyledAnimePage>
  );
};

const StyledAnimePage = styled.div`
  max-width: 80%;
  margin: 2rem auto;
  position: relative;
  & > a {
    position: absolute;
    top: 1rem;
    text-decoration: none;
  }
  & > h2 {
    font-weight: 400;
    letter-spacing: 3px;
    text-align: center;
    margin-bottom: 2rem;
  }
  & > .grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: max-content;
    & .anime {
      margin: 1rem;
      height: max-content;
    }
    & > p {
      margin: 2rem 0 4rem;
      font-size: 1.3rem;
      text-align: center;
    }
  }
`;
export default Animepage;
```
    

Above, we imported all the components and hooks we will be using. We also initialized the `[useParams](https://reactrouter.com/web/api/Hooks/useparams.)` hook, to get an anime title from our browser URL as a param based on the route we’ll define for this page. Next, we created 2 component states using `useState` hook, one to hold the quotes we will fetch from the API and the other for a loading, And in a `useEffect`, we are fetching the quotes based on the anime name gotten from the URL and setting the `quotes` state with it. We then returned a block of jsx styled with `styled-components`.

Above, you’ll notice that we didn’t use Recoil, instead, we saved the response we got from the API request in a `useState`, this is because the states will only be used in this component.


## Open Source Session Replay
Debugging a web application in production may be challenging and time-consuming. [OpenReplay](https://github.com/openreplay/openreplay) is an Open-source alternative to FullStory, LogRocket and Hotjar. It allows you to monitor and replay everything your users do and shows how your app behaves for every issue. 
It’s like having your browser’s inspector open while looking over your user’s shoulder.
OpenReplay is the only open-source alternative currently available.

![OpenReplay](https://raw.githubusercontent.com/openreplay/openreplay/main/static/replayer.png)

Happy debugging, for modern frontend teams - [Start monitoring your web app for free](https://github.com/openreplay/openreplay).

## Creating App Routes and Fetching Animes

To complete this app, Let’s navigate to `src/App.js`. Here, we will be doing 2 things:

1. Fetch a list of animes from the external API and updating the `animeTitles`  atom we created earlier with it.
2. Define our app routes with `react-router-dom`.

Let’s get started. Go to  `src/App.js` and **replace** what we have there with the code below.


```javascript
import { useEffect } from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import { useSetRecoilState } from "recoil";
import axios from "axios";
import { animeTitles } from "./store";
import Homepage from "./pages/home";
import Animepage from "./pages/anime";
```
    

Above, We imported

- `useEffect` - We will be making our API call inside it, so that we can fetch the array of anime once the page is rendered.
- `BrowserRouter`, `Route`, and `Switch` from `react-router-dom`  - We will create our routes with them.
- `[useSetRecoilState](https://recoiljs.org/docs/api-reference/core/useSetRecoilState)` from `recoil` - with this, we will update the `animeTitles` atom just by passing the atom to it as an argument, and
- `axios` - for fetching data from the external API

Next, we will create the App component and fetch the animes inside. To do that add the code below.


```javascript
const App = () => {
  const setTitles = useSetRecoilState(animeTitles);

  const fetchAnimes = async () => {
    try {
      const res = await axios.get(
        "https://animechan.vercel.app/api/available/anime"
      );
      setTitles(res?.data);
    } catch (error) {
      console.log(error?.response?.data?.error);
    }
  };

  useEffect(() => {
    fetchAnimes();
  }, []);
```
    

Above, we created the App component, and inside we created a variable `setTitles` with which we will update our `animeTitles` atom (state). Next, we created an async function called `fetchAnimes`, inside it, we fetched the animes from the external API using axios and updated our `animeTitles` state with it while using [try-catch](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch) for error handling. After that we called the `fetchAnimes` inside the `useEffect` we imported so this function runs once the page is rendered.

Let’s finish up the `App` component by adding routes.


```javascript
return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/" component={Homepage} />
        <Route exact path="/anime/:name" component={Animepage} />
      </Switch>
    </BrowserRouter>
  );
};

export default App;
```
    

We have now completed our app. let’s start our dev server to see how it works. 
Run the command below in your terminal


```
yarn start
```

OR


```
npm start
```


If you followed along correctly, you should see these pages. 

![localhost:3000 - homepage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k34w48hyexsdxibknznp.png)


Click on any anime and see a page like this. I will click on Naruto.


![anime page showing quotes from Naruto](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vxod82djlyt81jhzx7tr.png)



# Conclusion

In this article, we learned what Recoil is, why use it, and how to use it instead of Redux by building an anime-quote-generator app using Recoil for state management. We also compared how to do certain things in Recoil to Redux, and we saw how easy it is to use Recoil. You can learn more about Recoil from the official docs. 



# Resources
- [Github repository for tutorial project](https://github.com/Origho-precious/anime-quote-generator)
- [Recoil Official documentation](https://recoiljs.org/)
- [Redux Official documention](https://redux.js.org/)
- [List of Some React State Management libraries](https://openbase.com/categories/js/best-react-state-management-libraries)