At the React Europe 2020 Conference, Facebook software engineer Dave McCabe introduced Recoil, a new state management library.
Recoil, which is still experimental, is already being used in production environments for some of Facebook’s internal products. After all, it is the state management framework officially launched. I didn’t have time to study it carefully before, so I took a look during the National Day holiday and shared it with you.
State and Context
Suppose we have the following scenario: there are two components, List and Canvas. When a node in List is updated, the node in Canvas is also updated.
The most common way to do this is to distribute a state to the List and Canvas components through the parent component. Obviously, every time the state changes, all nodes will be updated in full.
Of course, we can also use the Context API, where we store the state of the node in a Context, and any descendant users of the Provider will be rerendered whenever the props in the Provider changes.
To avoid the problem of full rendering, we can store each child node in a separate Context, thus adding a layer of providers for each additional node.
But what if the child nodes are added dynamically? We also need to add the Provider dynamically, which will cause the whole tree to be re-rendered again, which is obviously not what we want.
The introduction of Recoil
Recoil itself was designed to solve the React global data flow management problem by adopting a decentralized design pattern for managing atomic states.
Recoil has proposed a new state management unit, Atom, that is updatable and subscriptable, and when an Atom is updated, each subscriptable component is rerendered with the new value. If you use the same Atom from multiple components, all of those components share their state.
You can think of Atom as a collection of states, and changing an Atom only renders a specific child component, not the entire parent component.
Why not Redux or Mobx?
React itself provides states that are difficult to share across components, so we often rely on other libraries such as Redux and Mobx to help manage states during development. These libraries are widely used and we don’t have any major problems, so why would Facebook introduce a new status management framework?
Redux and Mobx are fine, mainly because they are not React libraries themselves, and we rely on their capabilities for state management. Redux offers great state management capabilities, but it’s expensive to use, you need to write a lot of tedious code, and asynchronous processing or caching is not a feature of the library itself, or even external libraries.
Also, they don’t have access to React’s internal scheduler, while Recoil uses React’s own state behind the scenes to provide concurrent mode capabilities in the future.
Based on using
Initialize the
Components that use recoil status need to be wrapped with RecoilRoot:
import React from 'react';
import {
RecoilRoot,
atom,
selector,
useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; function App() { return ( <RecoilRoot> <CharacterCounter /> </RecoilRoot> ); } Copy the code
Define state
We’ve already mentioned the concept of Atom. Atom is a new state, but unlike traditional state, it can be subscribed to by any component. When an Atom is updated, each subscribed component is rerendered with the new value.
First let’s define an Atom:
export const nameState = atom({
key: 'nameState'. default: 'ConardLi'
});
Copy the code
This approach means that you don’t need to centrally define state like Redux, and you can decentralize the data definition anywhere like Mobx.
To create an Atom, you must provide a key, which must be unique in RecoilRoot scope, and a default value, which can be a static value, a function, or even an asynchronous function.
Subscribe and update status
Recoil uses Hooks to subscribe and update status, using the following three apis:
useRecoilState
: a similar to useStateHook
, can be taken toatom
The value of andsetter
函useSetRecoilState
: only obtainsetter
Function. If only this function is used, the state change does not cause the component to be re-rendereduseRecoilValue
: Only the status is obtained
import { nameState } from './store'
// useRecoilState
const NameInput = (a)= > {
const [name, setName] = useRecoilState(nameState);
const onChange = (event) = > {
setName(event.target.value); }; return <> <input type="text" value={name} onChange={onChange} /> <div>Name: {name}</div> < / a >;} // useRecoilValue const SomeOtherComponentWithName = () => { const name = useRecoilValue(nameState); return <div>{name}</div>; } // useSetRecoilState const SomeOtherComponentThatSetsName = () => { const setName = useSetRecoilState(nameState); return <button onClick={() => setName('Jon Doe')}>Set Name</button>; } Copy the code
The derived condition
Selector represents a set of derived states that allow us to create states that depend on other Atom. It has a mandatory GET function that works similarly to Redux’s ResELECT or MobX’s @computed.
const lengthState = selector({
key: 'lengthState'. get: ({get}) = > {
const text = get(nameState);
return text.length;
}, }); function NameLength() { const length = useRecoilValue(charLengthState); return <>Name Length: {length}</>; } Copy the code
Selector is a pure function: given a set of inputs, they should always produce the same result (at least for the life of the application). This is important because the selector may be executed once or more, may be restarted, and may be cached.
Asynchronous state
Recoil provides a way to map state and derived state to React components through data flow diagrams. The really powerful feature is that the functions in the diagram can also be asynchronous. This makes it easy to use asynchronous functions in asynchronous React component rendering functions. Using Recoil, you can seamlessly mix synchronous and asynchronous functionality in the selector’s data flow graph. Simply return the Promise from the selector GET callback, not the return value itself.
For example, if the user name is stored in a database we need to query, all we need to do is return a Promise or use an async function. If the userID changes, the new query is automatically re-executed. The results will be cached, so the query will only be executed once for each unique input (so make sure the selector is pure, otherwise the cached result will be inconsistent with the latest value).
const userNameQuery = selector({
key: 'userName'. get: async ({get= > {}) const response = await myDBQuery({
userID: get(currentUserIDState),
}); return response.name; }, }); function CurrentUserInfo() { const userName = useRecoilValue(userNameQuery); return <div>{userName}</div>; } Copy the code
Suspense Recoil recommends using Suspense, where all asynchronous states are captured, plus ErrorBoundary for error catching:
function MyApp() {
return (
<RecoilRoot>
<ErrorBoundary>
<React.Suspense fallback={<div>Loading...</div>} > <CurrentUserInfo /> </React.Suspense> </ErrorBoundary> </RecoilRoot> ); } Copy the code
conclusion
Recoil advocates decentralized state management, which is similar to Mobx and feels like Observable + computed. However, its API and core ideas are not as simple and understandable as Mobx, but rather complicated. There is a cost for beginners to get started.
Use the Hooks API to implement state management for Componnent. Use useMemo to create derived state. Recoil’s useRecoilState and selector are also more like encapsulation of useContext and useMemo.
Still, it’s Facebook’s official state management framework, with its high performance and ability to take advantage of React’s internal scheduling mechanisms, including the concurrency mode it promises to support soon, which is something to look forward to.
In addition, its own decentralized mode of managing atomic state, read-write separation, on-demand rendering, derived caching, and other ideas are well worth learning.
The last
If there are any mistakes in this article, please correct them in the comments section. If this article is helpful to you, please feel free to like, comment, share and hopefully help more people.
Facebook’s React status management library is a new version of Facebook’s Code Secret Garden