Before recoil, understand what is State Management?
Recoil is a 'state management library for React. Before we dive into Recoil, let's first understand what state management is.
A state is any data that describes how an application operates. State can be divided into two types: dynamic data injected from the outside (data received from the server) and application UI state (closed or open, etc.).
It seems that the description of state can be summarized as 'something that can change dynamically'.
React is a library that controls views based on state. Therefore, how to manage a state is a very important part of building a React application.
React's own state management
For compatibility and simplicity, it is better to use the state management function built into React itself rather than an external library. However, the state management logic of React has the following limitations.
- In order to share the state of a component, it is necessary to elevate the state to a common parent element. In severe cases, there may be a problem of having to go up to the top of the application (prop drilling).
- Context API is not suitable for storing dynamic data, and performance limitations are clear from an optimization point of view. Context API is closer to dependency injection than state management in that the local state of a component is raised to a common parent element and then passed to the bottom.Because of these two limitations, there is also a problem that it becomes difficult to divide the code between 'where states are created' and 'where states are used'.
Initially, React is a library that only cares about Views.
To solve these problems, 'global state management' libraries such as Redux, MobX, Recoil, Jotai, and Zustand have emerged.
Why I don't like Redux?
Redux is the most used global state management library. If you use Redux, you can manage the state-related logic of components more efficiently by separating them into different files, and you can manage the global state relatively easily.
I'm also new to Redux, but I don't really like it. It's too inconvenient. You have to generate a lot of boilerplate code in the beginning before you can use it. In addition, in order to handle asynchronous operations, additional packages must be used.
In summary, the development experience was very inconvenient.
To improve this, the Redux Toolkit came out, but I wanted to experience other state management libraries rather than studying anything to use redux.
So I looked for another alternative, and Recoil caught my eye.
Recoil
The reason Recoil caught my eye was that it was made by the Facebook team that created React, and the API was very similar to React's Hook, so it looked easy to use. And when I tried it, it didn't look easy, it was really easy.
Recoil manages the global state in units called 'atoms' . React components can subscribe to and update atoms. When Atom is updated, each subscribed component is re-rendered to reflect the new state.
Atom can be created as follows.
import { atom } from "recoil"
export const fontSizeState = atom ( {
key : "fontSizeState" ,
default : 16 ,
} ) ;
It's really simple.
To read or write atom in component, useRecoilState you can use hook called. useStateIt's almost like a hook.
import { useRecoilState } from "recoil"
import { fontSizeState } from "../atoms"
function FontButton ( ) {
const [ fontSize , setFontSize ] = useRecoilState ( fontSizeState ) ;
return (
< button onClick = { ( ) => setFontSize ( ( size ) => size + 1 ) } style = { { fontSize } } >
Click to Enlarge
< / button >
) ;
}
Recoil also provides a rather unusual state called 'selector'. Selectors are data derived from other global states, which can make dynamic data-dependent on other atoms or selectors. As a pure function used to compute derived data based on state, only a minimal set of states is stored in atoms, and all other derived data is computed through the function specified in the selector to prevent the preservation of the useless states.
In a component, selectors and atoms have the same interface and can be used the same.
// ...
export const fontSizeLabelState = selector ( {
key : 'fontSizeLabelState' ,
get : ( { get } ) => {
const fontSize = get ( fontSizeState ) ;
const unit = 'px' ;
return ` ${ fontSize } ${ unit } ` ;
} ,
} ) ;
// ...
function FontButton ( ) {
const [ fontSize , setFontSize ] = useRecoilState ( fontSizeState ) ;
const fontSizeLabel = useRecoilValue ( fontSizeLabelState ) ;
return (
< >
< div > Current font size : $ { fontSizeLabel } < / div >
< button onClick = { setFontSize ( fontSize + 1 ) } style = { { fontSize } } >
Click to Enlarge
< / button >
< / >
) ;
}
Usually, when processing derived data using only React's functions, especially when processing asynchronous data, useEffectis often used. By using Selector, asynchronous data can also be processed, so useEffectthe logic used for processing can be separated. Separation of concerns can be effectively achieved.
Using Recoil, you can create a data-flow graph composed of atoms and selectors. Recoil's official documentation describes this graph as an 'orthogonal directed graph' to the React tree. I understood that orthogonality means that state changes flow through the data-flow graph, and data is projected on the component corresponding to each state.
Other useful APIs are also provided, but will not be covered here.
Reviews
When I used it, I liked the following:
- Easy handling of asynchronous actions and compatibility with React Suspense
- Simple settings and very simple API
- Easy separation of concerns
But it's not just about advantages.
As there are not enough references and the version is still 0.7.2, the reliability of the API is insufficient compared to other libraries. So I think it might be a little difficult to actively use it in a very large project.
But despite the disadvantages, I like the advantages so much that I think I will use them a lot.