How to Switch from Redux to Recoil
Tags: react, javascript
Published: 2020-12-08 05:51:45

---
title: How to Switch from Redux to Recoil
published: true
description: Like Redux, Recoil is state management library in React. Let’s look at the differences and how to make the switch when you’re ready.
tags: react, javascript, reactjs
canonical_ref: https://www.telerik.com/blogs/how-to-switch-redux-to-recoil
cover_image: https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/templates/reactt2_1200x303.png?sfvrsn=3ddeaf3b_2
---

Managing complex state in React can sometimes be hard, which is why some of us use Redux or similar libraries like MobX to manage state in React. Recoil is another state management library that is closely modeled towards React’s Hooks API. It allows you to define shared state as **atoms**, and computed state which it refers to as **selectors**. If you want to learn about the limitation the team at Facebook faced and how they tried to solve it with recoil, you can watch this [video](https://www.youtube.com/watch?v=_ISAA_Jt9kI&ab_channel=ReactEurope).

One important note: Although many companies including Facebook are using Recoil, it is technically in an experimental state, and its API and functionality may change.

In this post, I'll show you how to switch from Redux to Recoil and along the way compare the differences. I'll be working with the TodoMVC example from Redux's [GitHub repository](https://github.com/reduxjs/redux/tree/master/examples/todomvc). You can download the zip file using this [link](https://downgit.github.io/#/home?url=https:%2F%2Fgithub.com%2Freduxjs%2Fredux%2Ftree%2Fmaster%2Fexamples%2Ftodomvc) I made for you 😉. Here's how the app works:

![recoil-demo.gif](https://ucarecdn.com/50a0a54c-5314-4a5a-acc8-77797a622a35/)

## Setting Up Recoil

The first step to use any JavaScript library is to add it to the project. You can add a reference using the HTML `<script>` tag, or install it via npm. Since you have downloaded an npm-style project, install Recoil by running `npm install recoil` or `yarn add recoil`.

Similar to using Redux where we wrap our root component with the `<Provider />`, we're going to replace that with `<RecoilRoot />` so that Recoil state is available to the child components.

Open **src/index.js** and import the `RecoilRoot` module.

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

Then update the render function as follows:

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

## Defining and Updating State

To represent a piece of state, you declare what is called an `atom`. Since we want to store a list of todos, we will create an atom with a default or initial state. Create a new **recoil** directory and add a new file named **todos** with the following content.

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

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

Now open **component/Header.js** and update it with this code:

```javascript
import React from "react";
import TodoTextInput from "./TodoTextInput";
import { useSetRecoilState } from "recoil";
import { todos } from "../recoil/todos";

const Header = () => {
  const setTodos = useSetRecoilState(todos);

  const save = (text) => {
    if (text.length !== 0) {
      setTodos((todos) => [
        ...todos,
        {
          id: Date.now(),
          text,
          completed: false,
        },
      ]);
    }
  };

  return (
    <header className="header">
      <h1>todos</h1>
      <TodoTextInput
        newTodo
        onSave={save}
        placeholder="What needs to be done?"
      />
    </header>
  );
};

export default Header;
```

This component displays a text input to collect new todos and save them. To add a new todo, we need a function that will update the contents of the `todos` state. We used the `useSetRecoilState()` hook to get a setter function which is used in the `save()` function. On line 11, we used the _updater_ form of the setter function so that we can create a new list based on the old todos. That is all we need to do to be able to collect and store todo items.

If you compare this to Redux, you would need to create action creators and reducers to update a piece of state, then connect the component to Redux store and dispatch actions. In Recoil, you define an atom to hold data, then use hooks API to interact with that data. If you're new to React and understand the hooks API, it should be quick to grasp Recoil because it's closely modeled to React's API, unlike Redux where you'd need to understand its style of unidirectional data flow.

### Derived State

The next section in the app to update is the `<MainSection />` component. It renders an input to mark all todos as completed, and also two extra components which we'll get to later. So, open **componenrs/MainSection.js** and update it with the code below:

```javascript
import React from "react";
import { useRecoilValue, useRecoilState } from "recoil";
import Footer from "./Footer";
import VisibleTodoList from "../containers/VisibleTodoList";
import { completedTodoCount, todos } from "../recoil/todos";

const MainSection = () => {
  const completedCount = useRecoilValue(completedTodoCount);
  const [todoList, setTodoList] = useRecoilState(todos);
  const todosCount = todoList.length;

  const clearCompleted = () => {
    setTodoList((previousTodos) =>
      previousTodos.filter((todo) => todo.completed === false)
    );
  };

  const completeAllTodos = () =>
    setTodoList((previousTodos) => {
      const areAllMarked = previousTodos.every((todo) => todo.completed);
      return previousTodos.map((todo) => ({
        ...todo,
        completed: !areAllMarked,
      }));
    });

  return (
    <section className="main">
      {!!todosCount && (
        <span>
          <input
            className="toggle-all"
            type="checkbox"
            checked={completedCount === todosCount}
            readOnly
          />
          <label onClick={completeAllTodos} />
        </span>
      )}
      <VisibleTodoList />
      {!!todosCount && (
        <Footer
          completedCount={completedCount}
          activeCount={todosCount - completedCount}
          onClearCompleted={clearCompleted}
        />
      )}
    </section>
  );
};

export default MainSection;
```

What we did here is that instead of connecting to Redux and calling `mapStateToProps` and `mapDispatchToProps`, we used two Recoil hooks, which are `useRecoilValue` and `useRecoilState`. The `useRecoilValue()` function is used to read the content of a state; in our case it's `completedTodoCount`. We want to get the `todos` state and also be able to update it. For that we use `useRecoilState()` to read `todos` and get a function to update it. We have two functions, `clearCompleted()` and `completeAllTodos()`, that are used to update the state.

We need to define the `completedTodoCount` state. This should be computed from the `todos` state. For that, we're going to create what's called **selector** in Recoil. Open **recoil/todos.js** and import _selector_ from the Recoil package.

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

Then define the selector as you see below:

```javascript
export const completedTodoCount = selector({
  key: "completedTodoCount",
  get: ({ get }) => {
    const list = get(todos);

    return list.reduce(
      (count, todo) => (todo.completed ? count + 1 : count),
      0
    );
  },
});
```

To define a selector, you call the `selector()` function with an object which contains the name for the state and a `get()` function that will compute and return a value. This function receives an object that has a `get()` function that can be used to retrieve data from other atoms or selectors.

## Filtering Todos

At this point, I've covered most basics of Recoil and you can see how it's different from Redux but closely modeled towards React's Hooks API. The rest of this post will just be adding code to make the app fully functional using Recoil.

The next component we'll work on is the `<FilterLink />` component. Open **containers/FilterLink.js** and update the file with the code below:

```javascript
import React from "react";
import { useRecoilState } from "recoil";
import Link from "../components/Link";
import { visibilityFilter } from "../recoil/todos";

export default ({ filter, children }) => {
  const [visibility, setVisibilityFilter] = useRecoilState(visibilityFilter);
  const setFilter = () => setVisibilityFilter(filter);

  return (
    <Link
      active={filter === visibility}
      setFilter={setFilter}
      children={children}
    />
  );
};
```

Here we're rendering the `<Link />` component which will render input used to select how to filter the todos that'll be displayed. We used a new state which we didn't create yet, so we'll add that in. Open **recoil/todos.js** and add the function below:

```javascript
import {
  SHOW_ALL,
  SHOW_COMPLETED,
  SHOW_ACTIVE,
} from "../constants/TodoFilters";

export const visibilityFilter = atom({
  key: "visibilityFilter",
  default: SHOW_ALL,
});
```

## Display Todos

The next thing to do is to display the todos based on the filter that's set. For that, we'll add a new selector and update the `<VisibleTodoList />` component. While you still have **recoil/todos.js** open, add the selector below to it.

```javascript
export const filteredTodos = selector({
  key: "filteredTodos",
  get: ({ get }) => {
    const filter = get(visibilityFilter);
    const list = get(todos);

    switch (filter) {
      case SHOW_COMPLETED:
        return list.filter((t) => t.completed);
      case SHOW_ACTIVE:
        return list.filter((t) => !t.completed);
      default:
        return list;
    }
  },
});
```

Open **containers/VisibleTodoList.js** and update the file with the code below:

```javascript
import React from "react";
import TodoList from "../components/TodoList";
import { filteredTodos, todos } from "../recoil/todos";
import { useRecoilValue, useSetRecoilState } from "recoil";

const VisibleTodoList = () => {
  const filteredTodoList = useRecoilValue(filteredTodos);
  const setTodos = useSetRecoilState(todos);

  const completeTodo = (todoId) => {
    setTodos((previousTodos) =>
      previousTodos.map((todo) =>
        todo.id === todoId ? { ...todo, completed: !todo.completed } : todo
      )
    );
  };

  const deleteTodo = (todoId) => {
    setTodos((previousTodos) =>
      previousTodos.filter((todo) => todo.id !== todoId)
    );
  };

  const editTodo = (todoId, text) => {
    setTodos((previousTodos) =>
      previousTodos.map((todo) =>
        todo.id === todoId ? { ...todo, text } : todo
      )
    );
  };

  return (
    <TodoList
      filteredTodos={filteredTodoList}
      actions={{ completeTodo, deleteTodo, editTodo }}
    />
  );
};

export default VisibleTodoList;
```

Here we added three functions to delete a todo, update it, or mark it as completed. We can consider these functions as a combination of actions and reducer functions in Redux. I decided to put the functions in the same file as the component that needs it, but you can extract them into a separate file if you wish.

At this point, we've updated the app to use Recoil instead of Redux. The last thing to do is to update **components/App.js**. Open this file and change the import statement for the `<Header />` and `<MainSection />` components.

```javascript
import Header from "./Header";
import MainSection from "./MainSection";
```

And there you have it, a todo app updated from using redux to recoil.

![recoil-demo.gif](https://ucarecdn.com/50a0a54c-5314-4a5a-acc8-77797a622a35/)

## Conclusion

Moving this app from Redux to Recoil was less complicated than I'd imagined. I guess this won't be the case for all your apps, based on how you designed your Redux state and a few other factors. But I think it's fairly easy to use for new apps because it's modeled after the React API that you're familiar with. 

You can learn more about Recoil on [recoiljs.org](https://recoiljs.org/). You can find the completed app with source code on [GitHub](https://github.com/pmbanugo/react-todomvc-recoil). 

Originally posted on [Telerik](https://www.telerik.com/blogs/how-to-switch-redux-to-recoil)