DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for ReactJS: State Persistence Using Recoil
Joie Llantero
Joie Llantero

Posted on • Updated on

ReactJS: State Persistence Using Recoil

Recoil is an open-source experimental state management library by Facebook. It's one of the many libraries that makes managing states easier like persisting the theme state (light/dark mode) of your website. Other examples of such libraries are Xstate, Redux, Zustand, and Jotai.

Getting Started

In your project, install Recoil using the npm command below.

npm install recoil
Enter fullscreen mode Exit fullscreen mode

or if you're using yarn.

yarn add recoil
Enter fullscreen mode Exit fullscreen mode

You can view more installation guides in the documentation here.

Next, we need to add Recoil to our index.js so that we can use it everywhere in our project.

...
import { RecoilRoot } from "recoil";

ReactDOM.render(
  ...
    <RecoilRoot>
        <AppRoutes />
    </RecoilRoot>
  ...
  document.getElementById('root')
);
Enter fullscreen mode Exit fullscreen mode

Creating an atom

Atoms contain the source of truth for our application state and below is an example of creating an atom.

export const darkModeState = atom({
  key: 'darkMode',
  default: false,
});
Enter fullscreen mode Exit fullscreen mode

For use cases like this, I like to put my atoms inside a globalState.js file inside the shared folder of my project. This is because many components will share this atom. Moreover, inside the atom that I made is a unique key and a default value. An atom key is a serializable value that labels the atom and it's useful especially when we want to persist our state. On the other hand, the default value is the default state of the atom.

Using the atom in your component is almost similar to useState hooks.

const [isDarkModeEnabled, setIsDarkModeEnabled] = useRecoilState(darkModeState);
Enter fullscreen mode Exit fullscreen mode

Instead of useState, we use useRecoilState and instead of passing the default state, e.g., false, we pass in our atom, i.e., darkModeState.

When we don't need to change the state of the atom and only need access to its value, we can simply do it like the code snippet below.

const isDarkModeEnabled = useRecoilValue(darkModeState);
Enter fullscreen mode Exit fullscreen mode

Saving the state to local storage

There are use cases when we want to keep states like saving the preferred theme to the website visitor's browser local storage. This helps returning visitors to keep their preferred settings that they set on their previous visit.

Below is an example of using Recoil to persist the theme of a website.

const localPersist = ({onSet, setSelf, node}) => {
  const storedData = localStorage.getItem(node.key)
  if (storedData != null){
    setSelf(JSON.parse(storedData))
  }
  onSet((newData, __, isReset) => {
    isReset
      ? localStorage.removeItem(node.key)
      : localStorage.setItem(node.key, JSON.stringify(newData));
  })
}

export const darkModeState = atom({
  key: 'darkMode',
  default: false,
  effects: [localPersist]
});
Enter fullscreen mode Exit fullscreen mode

Notice the effects parameter that I added to my atom. That parameter is called atom effects and it's an API for managing side-effects and synchronizing or initializing Recoil atoms. It's typically used in state persistence, synchronization, managing history, etc. As seen above, I passed the function that handles saving, removing, and getting the state from the browser's local storage

Furthermore, the localPersist function can be called by any atom. In other words, if you need a state to persist, you can just call the function to persist your state. Besides boolean values, the function also works for other data types like strings, objects, and arrays.

export const myStringState = atom({
  key: 'myString',
  default: '',
  effects: [localPersist]
});

export const myListState = atom({
  key: 'myList',
  default: [],
  effects: [localPersist]
});
Enter fullscreen mode Exit fullscreen mode

Inside the localPersist function we have the onSet function which gets called when our atom changes. To get the state from our local storage, we use the setSelf function. Moreover, the isReset function is used when the useResetRecoilState is called which removes the state from the browser's local storage.

Now that we've finished setting up the globalState.js, we now have states that are accessible to multiple components which also persists. You can check if it works by going to inspect element > Application tab > Storage > Local Storage > http://your-website-url.

Final Thoughts

Recoil is easy to implement and as seen earlier, it's similar to how we use useState. There are many new state management libraries currently existing and maybe more will be released. Jotai is an example of a state management library that is similar to Recoil–they are both atomic. Other types of libraries are flux (Redux, Zustand) and proxy (Mobs, Valtio, Overmind). At the end of the day, choosing the right library for your project depends on many variables like package size, use case, and more.


Click here to view a sample implementation of what we've discussed.

Top comments (4)

Collapse
biomathcode profile image
Pratik sharma

Guess what !!!!!! i am also writing a blog on recoil as well. I really like the library atleast for now πŸ˜„

I like zustand way of adding local storage with the middleware. That is pretty neat.

Collapse
joiellantero profile image
Joie Llantero Author

That's awesome! I totally agree that Recoil is a great library. Zustand is great as well and in terms of difficulty, it's much easier to use than Redux. Which brings me to Jotai because it's much simpler than Recoil. What do you think of Jotai?

Collapse
bearosari profile image
Bea Supanga

Great article! :)

Collapse
joiellantero profile image
Joie Llantero Author • Edited on

Thank you!

🌚 Browsing with dark mode makes you a better developer.

It's a scientific fact.