Learn about the new library introduced by Facebook called Recoil, which aims to solve a few problems such as shared state, derived data and queries, app-wide observation, and many more.
A developer never stops learning, especially in this time when new libraries and concepts are created almost every day. Part of a developer’s job is to always be aware of what’s happening, what’s been released, and to know if there’s something new being talked about in the community.
React is known for being the best and most-used JavaScript UI library now, and there’s a lot of reasons for that. One of the main reasons React is so popular is the community—it’s always creating and releasing something new that can improve the job of React developers.
This year at the React Europe 2020 conference, the React community was introduced to a new state management library created by Facebook called Recoil. Another state management library for React? What are the benefits of using this new library and not something more familiar and robust, with more examples and use cases, such as Redux?
So, in this article, we will learn more about this state management library called Recoil and understand its use cases, the differences from other state management libraries, and how we can start to use it in our projects.
Why Recoil?
Most state management libraries serve simple applications really well—those applications that don’t have many complex features and need the job to be done quickly. In some cases, we might need to use something more complex to solve a specific problem, and that’s when conventional libraries might not help us any further.
There’s nothing wrong with Redux or MobX, the most popular libraries for React applications. These libraries are useful and, in the majority of cases, they’re enough to keep your state data in order and allow your application to scale very well. The only problem with these libraries is that they require a lot of configuration and we need to set up a bunch of things before getting started, especially with Redux.
To work with Redux, for example, there’s a lot of work that needs to be done to set up a simple store before starting to manage your application state. Most of the time we use third-party libraries for things such as memorization, computed selector values, etc. In these specific cases, Redux cannot help us. So there’s a limit to what we can do and achieve with Redux alone.
The team inside Facebook that created Recoil faced some problems while working in internal app development. For most of the features that they needed, a conventional state management library could not help them, or they would waste a lot of time on it. Things like state sync between components, derived state, app-wide observation, etc.
A few points that Recoil has that make this new state management library very powerful:
- Shared state— Share the same state in different components in the React tree in a way that’s really performant and consistent.
- Derived data and queries — Compute things based on changing state efficiently in a very robust and bug-free way. Derived data are things that are computed or related to the state in some way.
- App-wide state observation — Observe changes, time-travel debugging, persistence, logging—observe everything that’s happening in the app from some component.
Here are the two core concepts that we should learn before starting to use Recoil:
Atoms
An atom is a changeable, subscribable unit of the state. Imagine atoms as a local React state, which any component can subscribe to. Atoms are updatable and subscribable, and changing the value of an atom will re-render every component that’s subscribed to that specific atom. All the components that are subscribed to an atom are sharing the same state.
This is how we can create an atom using Recoil:
const loadingState = atom({
key: 'loadingState',
default: false
});
To create an atom we need to provide a key
, which should be a unique value. This key
is used for persistence, debugging, etc. Also, we need to provide the default
value of our atom, it can be anything such as arrays, objects, strings, functions, etc.
For a component to subscribe to an atom, we need to use the useRecoilState
hook. It’s a hook similar to the useState
from React, but inside this hook, we pass the atom that we want to subscribe to.
import { atom } from 'recoil';
const loadingState = atom({
key: 'loadingState',
default: false
});
const App = () => {
const [loading, setLoading] = useRecoilState(loadingState);
...
}
Sometimes, we just want to return the value of a specific state. This is very possible and simple to do with Recoil. We can return only the value of an atom, without the setter function, using the useRecoilValue
hook.
import { atom } from 'recoil';
const loadingState = atom({
key: 'loadingState',
default: false
});
const App = () => {
const loading = useRecoilValue(loadingState);
...
}
Selectors
A selector is a pure function that can receive an atom or a selector as an input. Given an input, the selector returns a modified state every time the upstream atoms or selectors are updated. Selectors can also be subscribed to, and again, when the selector changes, every component that’s subscribed to that specific selector will be re-rendered.
To create a selector, we need to provide a key, which needs to be a unique value and a get
function. This get
function returns a modified piece of an atom.
import { selector } from 'recoil';
const checkLoadingState = selector({
key: 'loadingState',
get: ({ get } ) => {
const loading = get(loadingState)
return `Loading is ${loading ? "true" : "false}`
});
Recoil has a pretty simple and powerful API, so everyone can get started easily and quickly with this new state management library. Now that we know a little bit about Recoil, let’s build something so we can see how it works in practice.
## Getting Started
Now that we know the basics of Recoil, the best way to understand it is by creating something. Let’s create an example where we can share the state of our logged-in user between components.
First, let’s create a new create-react-app
:
create-react-app recoil-example
Now, let’s install Recoil:
yarn add recoil
In order to use Recoil state, we need to wrap our desired components with a root component called RecoilRoot
. Now, we’re able to use Recoil state inside the components that are inside RecoilRoot
.
In our App component, we’re going to import the RecoilRoot
and put all our components inside it:
import { RecoilRoot } from 'recoil';
const App = () => {
return (
<_RecoilRoot_>
...
</_RecoilRoot_>
);
}
Now, before we create our components to show our state, we’re going to create an atom and a selector. Let’s create a file called helpers
. Inside this file, we will import the atom
and selector
functions from Recoil.
import { atom, selector } from "recoil";
Here’s what we want to do, so things will get clear. We want to create an atom where we can get the current logged-in user. We’re going to create an atom called loggedInUserState
and, as a default value, we can pass any name we want.
export const loggedInUserState = atom({
key: 'loggedInUserState',
default: {
name: "John"
}
})
Now, we’re going to create our selector. Our selector will just return a message by getting the name of the current logged-in user. We’re going to create a selector called loggedInUserSelector
, and this is how it will look:
export const loggedInUserSelector = selector({
key: 'loggedInUserSelector',
get: ({ _get_ }) => {
const user = get(loggedInUserState)
return `Hello ${user.name}, you're logged in!`
}
})
Inside our loggedInUserSelector
selector, we’re using the get
function to get the current logged-in user passing our loggedInUserState
atom and returning a message.
We’re going to create two components: Header
and Dashboard
. Inside our Header
component, we will just show the current logged-in user. Let’s create our Header
component, like this:
import React from "react";
const Header = () => {
return (
<header>
<h3>Logged in</h3>
</header>
)
};
export default Header;
Inside our Header
component, we’re going to import our loggedInUserState
atom and a hook from Recoil. We’re going to use the useRecoilValue
since in this Header
component we’re just going to show the current logged-in user.
Let’s import our loggedInUserState
and constant called loggedInUser
and display the name of the current logged-in user inside our h3
element. Inside our useRecoilValue
, we’re going to pass our loggedInUserState
atom, which means that now this component is subscribed to this atom, so every time this atom changes this component will be re-rendered.
import React from "react";
import { useRecoilValue } from 'recoil';
import { loggedInUserState } from "./helpers";
const Header = () => {
const loggedInUser = useRecoilValue(loggedInUserState);
return (
<header>
<h3>Logged in: {loggedInUser.name}</h3>
</header>
)
};
export default Header;
We now have our Header
component working fine. Now, let’s create our Dashboard
component. Inside this component we will be able to show and change the name of the current logged-in user.
This is how our Dashboard
component will look at first:
import React from "react";
const Dashboard = () => {
return (
<main>
<h3>Hello. You're logged in</h3>
<h3>""</h3>
<input _type_="text" _value_="" _onChange_={() => {}} />
<button >Submit</button>
</main>
)
};
export default Dashboard;
Let’s import some things now. We’re going to import the useState
hook from React to get the value of our input, the useRecoilValue
and useRecoilState
from Recoil, and our loggedInUserState
atom and loggedInUserSelector
selector.
We’re going to use the useRecoilState
to get our current logged in user and a setter function to set a new user. The useRecoilValue
will just return the current logged in user.
const [user, setUser] = useState('');
const [loggedInUser, setLoggedInUser] = useRecoilState(loggedInUserState);
const userLoggedIn = useRecoilValue(loggedInUserSelector);
Now, we’re going to create a function called onChange
to get the actual value of the input, and a function called loginUser
, which we will use to set the new name of the logged-in user.
const [user, setUser] = useState('');
const [loggedInUser, setLoggedInUser] = useRecoilState(loggedInUserState);
const userLoggedIn = useRecoilValue(loggedInUserSelector);
const onChange = ({ target: { _value_ }}: _any_) => {
setUser(_value_);
};
const loginUser = () => {
setLoggedInUser({ name: user })
};
This is how our final Dashboard
component should look like:
import React, { useState } from "react";
import { useRecoilState, useRecoilValue } from 'recoil';
import { loggedInUserState, loggedInUserSelector } from "./helpers";
const Dashboard = () => {
const [user, setUser] = useState('');
const [loggedInUser, setLoggedInUser] = useRecoilState(loggedInUserState);
const userLoggedIn = useRecoilValue(loggedInUserSelector);
const onChange = ({ target: { _value_ }}: _any_) => {
setUser(_value_);
};
const loginUser = () => {
setLoggedInUser({ name: user })
};
return (
<main>
<h3>Hello, {loggedInUser.name}. You're logged in</h3>
<h3>{userLoggedIn}</h3>
<input _type_="text" _value_={user} _onChange_={onChange} />
<button _onClick_={loginUser}>Submit</button>
</main>
)
};
export default Dashboard;
We’re now able to change the name of the current logged-in user. Recoil is, in fact, really awesome. In an application that has a lot of contexts, Recoil can do some magic and replace a lot of code with some simple and powerful atoms and selectors.
Should I Use it?
That’s a question that a lot of developers ask themselves every time they see a new library released. First, you need to understand the point that it solves before starting to migrate your whole project to Recoil.
Here are some points that might help you decide if you should use it or not:
Is your application going to need to share state between components, and you don’t want to rely on something like React Context? Recoil might be a good solution for this.
Does your application need to keep sync from the state between components, need persistence between URLs, and observe everything that’s happening in your React tree? Recoil might be a good solution for this.
Conclusion
In this article, we learned more about a new state management library introduced by Facebook called Recoil. Recoil brings the concepts of atoms and selectors. Atoms are pieces of React state that can be subscribed to by any component inside the root component. Selectors are pure functions that can receive atoms and selectors and return derived state.