DEV Community

Cover image for Manage global state with Recoil.js
Eduardo Alvarez
Eduardo Alvarez

Posted on • Updated on

Manage global state with Recoil.js


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, 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

Alt Text
Figure 1: Application to do.

Alt Text
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
  2. Setup App.jsx
  3. Export State Variables
  4. Import State Variables

 

1. Install Recoil

First, we need to install Recoil using NPM inside your project folder.

npm install recoil
Enter fullscreen mode Exit fullscreen mode

 

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.

// App.jsx

import { RecoilRoot } from "recoil";

export default function App() {
  return (
    <div className="App">
      <RecoilRoot>
        <h1>Pet shop</h1>
      </RecoilRoot>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode
  1. import { RecoilRoot } from "recoil" to enable the Recoil library
  2. <RecoilRoot> needs to wrap around any parent or child component that may need access to the global state.

 

3. Export state variables

I'm showing one 1 state file, but this applies to any other state file as well.

// state/userData.js

import { atom } from "recoil";

export const petState = atom({
  key: "petState",
  default: "No pet choosen",
});
Enter fullscreen mode Exit fullscreen mode
  1. import { atom } from "recoil": A Recoil atom is a JavaScript Object used to hold the data you want as a global state variable.
  2. export const petState To indicate that we will import this variable into other files.
  3. 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.
    2. default: The initial value of this state. It can be a string, boolean, array, object, etc.

 

4. Import state variables

I'm showing the Content component, but this applies to the App, Header, and Footer components.

// 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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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.
  2. import { petState } from "../state/userData" Here we import the variable we created in the State file.
  3. 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 and open the branch global-state.


Additional reading

  • Recoil documentation: To learn more tricks that Recoil can do.
  • Context API: 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: 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


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.

// Valid
<div className="App">
  <BrowserRouter>
    <RecoilRoot>
      <Switch>
        <Route component={Page} />
      </Switch>
    </RecoilRoot>
  </BrowserRouter>
</div>;
Enter fullscreen mode Exit fullscreen mode
// Also valid
<div className="App">
  <RecoilRoot>
    <BrowserRouter>
      <Switch>
        <Route component={Page} />
      </Switch>
    </BrowserRouter>
  </RecoilRoot>
</div>;
Enter fullscreen mode Exit fullscreen mode

Alt Text


Credits

Latest comments (0)