Whaaats up, people! It's nice to have you here again!!!
This is the fourth and penultimate part of articles of react management. How are you feeling with so much technologies? Amazing, isn’t it?
we are almost at the end of our journey!
I hope you guys liked the previous article where I talk about state management using the Jotai 👻. I bring the concept of atoms and selectors and this will be very important for this article. Because, Jotai was inspired by Recoil, so the same concepts of atoms we will use here.
If you did not saw the previous part you can find here: Jotai State Management
We are going to create a simple application but we will be able to see the code of the technologies and compare the implementation.
Just to remember, let's get situated:
- Context API (part 1)
- Redux (part 2)
- Jotai (part 3)
- Recoil (part 4)
- Zustand (part 5)
Summary:
Introduction
According to the documentation of Recoil:
Recoil defines a directed graph orthogonal to but also intrinsic and attached to your React tree. State changes flow from the roots of this graph (which we call atoms) through pure functions (which we call selectors) and into components.
Recoil provide the minimal and simple API's to we manage our state (remember some other state manager cof cof jotai). This library is so simple that I don't have so much say aaas Jotai, so let's implement and I will cover some concepts beyond we code.
Just to remember again, if you did not saw the first part, I created all components that we will use in this and next parts on the first part. Soooo if you did not saw yet, I recommend to see on: State Management on React [Part 1] - Context API.
We will use the first version of the components that we created without the Context API integration.
Now with all concepts at hands, LET'S TO THE CODE!
Show me the code
Let's add the dependencies that we will use:
yarn add recoil
Yeah, easy peasy!
Let's start adding the Provider of recoil, called RecoilRoot
on our main three of components.
main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { RecoilRoot } from "recoil";
import { ButtonChangeTheme } from "./components/ButtonChangeTheme/index.tsx";
import { Content } from "./components/Content/index.tsx";
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<RecoilRoot>
<ButtonChangeTheme label="Change Theme" />
<Content text="Hello World!" />
<App />
</RecoilRoot>
</React.StrictMode>
);
I will create a directory on /src
called store
and for each feature I will create a folder with the name of the feature. So, we have two features in our app: change theme color (dark mode) and a todo list:
I will start with the dark mode feature. We will create a file on store/theme/index.ts
and create this code:
import { atom } from "recoil";
export const isDarkState = atom({
key: "IsDark",
default: false,
});
That's it! What?
Yes! Minimalistic and simple.
We will provide a key and the value of the atom. But we will we see soon, that it's not so simple and there's magic here. We need to manipulate this state.
Now let's see how the todo store will it be.
Let's create onstore/theme
two files. First the types types.ts (my preference) and the index.ts.
The types.ts contains just the type of our todo:
export interface ITodo {
id: number;
text: string;
done: boolean;
}
I will create a file on index.ts and the code will be:
import { atom } from "recoil";
import { ITodo } from "./types";
export const todoListState = atom<ITodo[]>({
key: "TodoList",
default: [],
});
Woww, simple we created our "store" files using the atoms. Notice, that de default value it's an array, because we will provide a list o todo items.
Now we will change the components to use our states and atoms.
First, let's change the ButtonChangeTheme:
components/ButtonChangeTheme/index.tsx
import { useSetRecoilState } from "recoil";
import button from "./button.module.css";
import { isDarkState } from "../../store/theme";
interface ButtonChangeThemeProps {
label: string;
}
export const ButtonChangeTheme = ({ label }: ButtonChangeThemeProps) => {
const setChangeColor = useSetRecoilState(isDarkState);
const handleChangeColor = () => {
setChangeColor((color) => !color);
};
return (
<button className={button.btn} onClick={handleChangeColor}>
{label}
</button>
);
};
Here, we have the first difference of jotai. Recoil will provide a hook to set the state, called useSetRecoilState
, where we will pass the atom that we created. And the business rules it will be stay on component.
Now changing the Content
dummy component:
components/Content/index.tsx
import { useRecoilValue } from "recoil";
import { isDarkState } from "../../store/theme";
interface ContentProps {
text: string;
}
export const Content = ({ text }: ContentProps) => {
const isDark = useRecoilValue(isDarkState);
return (
<div
style={{
height: "30vh",
width: "100vw",
color: isDark ? "#fff" : "#111827",
backgroundColor: isDark ? "#111827" : "#fff",
}}
>
{text}
</div>
);
};
To use the state, Recoil will provide the hook useRecoilValue
and we will pass the atom that we created.
Our FormTodo
component, let's just use the atoms that we created to store the todo that we type and add a new todo item. But again, the business logic will stay on component:
components/FormTodo/index.tsx
import style from "./Form.module.css";
import { Button } from "../Button";
import { Input } from "../Input";
import { useState } from "react";
import { useSetRecoilState } from "recoil";
import { todoListState } from "../../store/todo";
import { ITodo } from "../../store/todo/types";
export const FormTodo = () => {
const [todo, setTodo] = useState("");
const setTodoState = useSetRecoilState(todoListState);
const handleAddTodo = () => {
const newTodo: ITodo = {
id: Math.random() * 1000,
text: todo,
done: false,
};
setTodoState((state) => [...state, newTodo]);
setTodo("");
};
return (
<div className={style.formContainer}>
<Input
value={todo}
label="Todo"
onChange={(evt) => setTodo(evt.target.value)}
/>
<Button label="Add" onClick={handleAddTodo} />
</div>
);
};
To store the value of input value I will be using the useState
. And to add on todo list we will copy the state, using the spread operator and call the hook useSetRecoilState
.
And finnaly, let's change the ListTodo
component to use the atom to remove a todo item and retrieve the todos:
components/ListTodo/index.tsx
import { useRecoilValue, useSetRecoilState } from "recoil";
import style from "./ListTodo.module.css";
import { todoListState } from "../../store/todo";
export const ListTodo = () => {
const todos = useRecoilValue(todoListState);
const setTodoState = useSetRecoilState(todoListState);
const handleRemoveTodo = (id: number) => {
const removed = todos.filter((todo) => todo.id !== id);
setTodoState(removed);
};
return (
<ul>
{todos.map((todo) => (
<li className={style.item} key={todo.id}>
<label>{todo.text}</label>
<i
className={style.removeIcon}
onClick={() => handleRemoveTodo(todo.id)}
/>
</li>
))}
</ul>
);
};
Here, we are using both hooks that I introduced to you useRecoilValue
to get the todoList and useSetRecoilState
to set the new list with the todo item removed.
and we finish one more time the same app LOL!
Results
Now we can see how we use Recoil, offering the minimum API to manage our state in a exact manner, without unnecessary re-renders.
But what I introduced to you it's just the basic of recoil. Read the documentation to see more details:
The visual will not change but let's see 👀 using the React Dev Tools with highlighting render:
And the React Dev Tools Profiler:
Conclusion
Recoil offers a minimal API to manager our state. It simple and easy to maintain our state.
It's easy to see jotai's inspiration
I'm not saying that recoil is the best library and that you should use it in your project. So much so that the purpose of these articles is to compare the application and implementation of each state manager.
So in summary, in this article we saw how we can implement the recoil, creating atoms and manipulate our global state using the hooks that the recoil provides.
Some references:
That's all folks!
I hope you enjoyed it and added some knowledge. See you in the next and last part, where we will talk about Zustand! 💜
Top comments (0)