Manage global state with Recoil.js
Tags: react
Published: 2021-03-16 09:45:14

{% youtube hEJdVUwAHPQ %}
*Video tutorial of this article*

So far, we have built small projects. In the prop article, we learn how to pass information from a parent to a child. But what happens if we need to pass information between siblings. You can create the state on the parent and then send it to each child, but at one point, your main component will be full of states that should belong somewhere else.

Enter global state management, a way to create state variables in a separate file that any component can import without receiving data using props.

In this article, we will use [Recoil.js](https://recoiljs.org), a library created by one Facebook developer working in the React team that is the easiest way to implement global state management.

---

# Intended result

[Interactive demo](https://fe-react-router-dom.web.app)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/et3fzhmxslyev66fpzt9.png)
Figure 1: Application to do. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ztiir7n6fjl7d4k49782.png) 
Figure 2: App hierarchy chart. Click the image to zoom it to read the description of each component.

**Notes:** 
* The global state is floating around to denote its independence to the component diagram.
* The state file extensions are `.js` because they are plain JavaScript files instead of React components.
* We could have a single JavaScript file for both global state variables, but I want to teach how to import and export variables in different files.
* Each Recoil variable must have its own unique name, even if you have the variables in different files.

---

# Getting started
To work with Recoil, we need to do these steps:
1. [Install Recoil](#guide-install)
1. [Setup App.jsx](#guide-setup)
1. [Export State Variables](#guide-export)
1. [Import State Variables](#guide-import)

<span id="guide-install">&nbsp;</span >
### 1. Install Recoil
First, we need to install Recoil using NPM inside your project folder.
```
npm install recoil
```

<span id="guide-setup">&nbsp;</span >
### 2. Set up App.jsx
This step needs to be done in the `App.jsx` regardless of where we need to use global state data.

```jsx
// App.jsx

import { RecoilRoot } from "recoil";

export default function App() {
  return (
    <div className="App">
      <RecoilRoot>
        <h1>Pet shop</h1>
      </RecoilRoot>
    </div>
  );
}
```

1. `import { RecoilRoot } from "recoil"` to enable the Recoil library
1. `<RecoilRoot>` needs to wrap around any parent or child component that may need access to the global state.

<span id="guide-export">&nbsp;</span >
### 3. Export state variables
I'm showing one 1 state file, but this applies to any other state file as well.

```javascript
// state/userData.js

import { atom } from "recoil";

export const petState = atom({
  key: "petState",
  default: "No pet choosen",
});
```

1. `import { atom } from "recoil"`: A Recoil atom is a JavaScript [Object](https://www.w3schools.com/js/js_objects.asp) used to hold the data you want as a global state variable.
1. `export const petState` To indicate that we will import this variable into other files.
1. `atom({})` The object that set up this global state variable. Its parameters are:
  1. `key`: The unique id of this state. To avoid confusion, use the same name as the constant.
  1. `default`: The initial value of this state. It can be a string, boolean, array, object, etc.

<span id="guide-import">&nbsp;</span >
### 4. Import state variables
I'm showing the Content component, but this applies to the App, Header, and Footer components.

```jsx
// components/Content.jsx

// Core
import { useRecoilState } from "recoil";

// Internal
import { countState } from "../state/siteSettings";
import { petState } from "../state/userData";

export default function HeaderBar() {
  // Global state
  const [count, setCount] = useRecoilState(countState);
  const [pet, setPet] = useRecoilState(petState);

  // Method
  function onClear() {
    setPet("");
    setCount(0);
  }

  return (
    <header className="header">
      <span className="pet-choosen">{pet}</span>
      <button onClick={onClear}>Clear choice</button>
    </header>
  );
}
```

This one is longer but lets take our time to analyze it:
1. `import { useRecoilState } from "recoil"` Instead of using React useState to handle a local state, we use Recoil to handle a global state.
1. `import { petState } from "../state/userData"` Here we import the variable we created in the State file.
1. `useRecoilState(petState);` instead of using `useState` we use `useRecoilState`. `petState` is the initial value that we imported in the previous step.

You can see that inside the JSX and the function that controls the button, the code is the same as using the useState hook.

---

# Conclusion

Recoil has a key advantage over other global state management libraries: It looks and behaves exactly like React hooks. Thus, making it easy to mix them without needing to learn that much.

If you want to see the finished code, open this [link](https://github.com/elalienx/sda-react-articles) and open the branch global-state.

---

# Additional reading
* [Recoil documentation](https://recoiljs.org/docs/introduction/installation): To learn more tricks that Recoil can do.
* [Context API](https://reactjs.org/docs/context.html): The current way to do global state management on React. Is not hard to learn, but it does not organize your code as elegantly as Recoil. 
* [Redux](https://redux.js.org): The original way to handle global state on React. Please do not click on it unless you want to have nightmares. It is the equivalent of the SE module (SDA students understand this inside joke) but on code.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kljeyn6gnrg9q0pijqyd.png)

---

# Combining Recoil with React Router DOM
If you have `<BrowserRouter>` to handle navigation, it does not matter if Recoil wraps BrowserRouter or the other way around.

```jsx
// Valid
<div className="App">
  <BrowserRouter>
    <RecoilRoot>
      <Switch>
        <Route component={Page} />
      </Switch>
    </RecoilRoot>
  </BrowserRouter>
</div>;
```

```jsx
// Also valid
<div className="App">
  <RecoilRoot>
    <BrowserRouter>
      <Switch>
        <Route component={Page} />
      </Switch>
    </BrowserRouter>
  </RecoilRoot>
</div>;
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/51fj5ocgrucjjqv5tu5i.png)

--- 

# Credits
* Cover picture: Photo by <a href="https://unsplash.com/@echaparro?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Edgar Chaparro</a> on <a href="/s/photos/globe?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
* Video tutorial: [RecoilJS - State Management Library For React](https://www.youtube.com/watch?v=hEJdVUwAHPQ) by [
Maksim Ivanov](https://www.youtube.com/channel/UC5hby9iDkwOTQM7PIjyjbgw).