This article introduces Recoil (not react), facebook’s state management library.

Its advantages

  1. Avoid the overhead of libraries like Redux and Mobx.
  2. Circumvent the limitations of Context.

The downside:

  1. Currently, only hooks are supported.
  2. In the experimental stage, the stability remains to be observed.

The introduction

Redux

Let me show you a familiar picture. State management for Redux is shown below.

Mobx

  • Observable State, all values that can be changed.

  • Derivation:

    • Computed Value(Derivation) is the values that can be derived from the current observable state using pure functions.
  • Reaction, like Computed Value, is based on Observable State. A side effect that needs to happen automatically when state changes, which is used to link imperative programming with responsive programming, and ultimately requires I/O operations such as sending requests, updating pages, etc.

  • Action, all Observable State changes, user events, backend data pushes, etc.

  • Note: Variable data streams. To manage react state by Mutable, refer to the Mobx Documentation.

The connection and difference between the two:

  • Programming: Redux is more functional programming, Mobx is more object-oriented programming and responsive programming.
  • Data is stored differently: Redux stores data in a single store, while Mobx stores data in scattered stores.
  • Form of state storage:
    • Redux stores js native object form: requires manual tracking of state changes.
  • Mobx wraps this state as an observable and automatically tracks updates to this state.
  • Whether the data is mutable: Redux tends to use immutable state rather than modifying it directly, instead returning a new state using pure functions. State in Mobx can be changed directly. Juejin. Cn/post / 684490…

The State and the Content

Problem: Problems with State and Content

Scenario: There are two components: List and Canvas. The nodes in List are updated, and the corresponding nodes in Canvas are also updated.

The first way: passes State to the public parent node.

Disadvantages: full re-render.

The second method: Add Provider to the parent node and add Consumer to the child node, but each additional item adds another layer of Provider.

I. Introduction:

One of the biggest headaches when building a React application is state management. Although there are mature state management libraries such as Redux and Mobx, the overhead of using them is incalculable. The ideal approach, of course, is to use React for state management. But this raises three questions. Component state can only be shared with its ancestor components, which can incur significant redrawing overhead in the component tree. Context can only hold a specific value, rather than sharing an indefinite set of values with its Consumer. These two points make it very difficult to split the code between the components at the top of the component tree (state producers) and the components at the bottom of the component tree (state consumers)

Recoil defines an orthogonal and cohesive unidirectional graph in the component tree. State changes enter components through pure selectors from atoms at the bottom of the spectrum in the following way. The idea: Separate the state from the component to form a component-independent state tree, with Atom entering the component via selectors at the bottom. As shown in the figure.

Provides dependency free methods that expose the same GET /set interface as the React local state (simply translated as concepts like reducers). We were able to work with some of React’s new features, such as concurrency mode. State definitions are scalable and distributed, and code splitting is possible. Data state can be derived without modifying the component. Derived data state supports both synchronous and asynchronous. Think of jumps as a first-level concept, and you can even code the flow of state in a link. Therefore, it is easy to persist the state of the entire application in a backward compatible manner, so that the persistent state can be preserved when changes are applied.

Think of Atom as a collection of states, and changing an Atom only renders a specific child component, not the entire parent component. In contrast to Redux and Mobx, Redux and Mobx do not have access to the React internal scheduler. Recoil uses the React state in the background.

Ii. Main concepts

Atoms – Shared state

The smallest unit of state that a component can subscribe to – can be defined and updated similar to state in setState. (Generally define some basis)

const todoListState = atom({
  key: 'todoListState'.// Key is unique in RecoilRoot scope
default: [],});Copy the code

Selector (Derived state) – Pure function

A selector represents a derived state (derived from the underlying state atom). The inputs are pure functions of type Atoms/Selector. When its upstream changes, it automatically updates. It is used in much the same way as Atom.

const fontSizeLabelState = selector({
  key: 'fontSizeLabelState'.get: ({get}) = > {
const fontSize = get(fontSizeState);
const unit = 'px';
return `${fontSize}${unit}`;
  },
  set: ({get, set},newValue) = > {
      return set(' ',newValue)
  },
});
Copy the code
  • Key: The same function as atom’s Key is unique.
  • Get property: defines the value. Is a calculation function that uses the get field to access the input Atom and Selector. When the state on which it depends is updated, the changed state is updated.
  • Set: An optional function that returns a new writable state.

Note: Only a selector with both get and set has a read-write attribute. Set: a function that sets atomic values.

The hooks

  • UseRecoilValue () : Reads Atom/Selector (some selectors have only readable attributes and no writable attributes).
function TodoList() {
const todoList = useRecoilValue(todoListState);
return (
    <>
      <TodoItemCreator />
      {todoList.map((todoItem) => (
        <TodoItem key={todoItem.id} item={todoItem} />
      ))}
    </>
  );
}
Copy the code
  • UseSetRecoilState () : Writes to Atom/Selector.

Other related hooks

function TodoItemCreator() {
const [inputValue, setInputValue] = useState(' ');
const setTodoList = useSetRecoilState(todoListState);
const addItem = () = > {
    setTodoList((oldTodoList) = > [
      ...oldTodoList,
      {
        id: getId(),
        text: inputValue,
        isComplete: false,}]); setInputValue(' ');
  };
const onChange = ({target: {value}}) = > {
    setInputValue(value);
  };
return (
    <div>
      <input type="text" value={inputValue} onChange={onChange} />
      <button onClick={addItem}>Add</button>
    </div>
  );
}
// utility for creating unique Id
let id = 0;
function getId() {
return id++;
}
Copy the code
  • UseRecoilState (): Reads and writes atoms.
  • UseResetRecoilState () : resets the atomic default.

UseSetRecoilState differs from useRecoilState in that changes in the data flow do not cause the component Rerende, useSetRecoilState simply writes to the atom, does not subscribe to it and does not update it.

Note: All Atom is in a read-write state.

<RecoilRoot … props>

Global data flow management needs to be in the RecoilRoot scope. When nested, the innermost layer will nest the outer scope.

Three. Asynchronous processing:

  • Sync

In synchronous state, it will automatically change as long as the upstream data changes. As shown above.

  • Async

All you need is for the get function to return a promise. Recoil for asynchronous processing needs to work with React Suspense for asynchronous data. If any of the dependencies change, the selector is recalculated and a new query is executed. The results are cached, the same input is not queried, and the same input is only queried once.

  • Example:
const currentUserNameQuery = selector({
  key: 'CurrentUserName'.get: async ({get}) => {
const response = await myDBQuery({
      userID: get(currentUserIDState),
    });
returnresponse.name; }});function CurrentUserInfo() {
const userName = useRecoilValue(currentUserNameQuery);
return <div>{userName}</div>;
}
// Being in a pending state throws promises to suspense.
function MyApp() {
return (
    <RecoilRoot>
      <React.Suspense fallback={<div>Loading...</div>} ><CurrentUserInfo />
      </React.Suspense>
    </RecoilRoot>
  );
}
Copy the code
  • The asynchronous state can be caught by Suspence.
  • An error reported by an asynchronous process can be caught by ErrorBoundary.

Do not use Suspence

Instead of using Suspect to deal with an asynchronous selector, you can also use the useRecoilValueLoadable() Api in the current component.

function UserInfo({userID}) {
const userNameLoadable = useRecoilValueLoadable(userNameQuery(userID));
switch (userNameLoadable.state) {
case 'hasValue':
return <div>{userNameLoadable.contents}</div>;
case 'loading':
return <div>Loading...</div>;
case 'hasError':
throwuserNameLoadable.contents; }}Copy the code

Asynchronous requests can be read through the state of state.

Rely on external variables for queries

Sometimes you need to use parameters other than Atom/Select for data queries.

const userNameQuery = selectorFamily({
  key: 'UserName'.get: (userID) = > async ({get}) => {
const response = await myDBQuery({userID});
if (response.error) {
throw response.error;
    }
returnresponse.name; }});function UserInfo({userID}) {
const userName = useRecoilValue(userNameQuery(userID));
return <div>{userName}</div>;
}
Copy the code

Four Utils.

  • atomFamily()
    • Similar to auTom (), except atomFamily returns a function that takes a single argument. You can provide different Atom based on this parameter.
const elementPositionStateFamily = atomFamily({
  key: 'ElementPosition',
default: [0, 0],
});
function ElementListItem({elementID}) {
const position = useRecoilValue(elementPositionStateFamily(elementID));
return (
    <div>
      Element: {elementID}
      Position: {position}
    </div>
  );
}
Copy the code
  • The default value can be changed depending on the parameters passed in.
Const myAtomFamily = atomFamily({key: 'MyAtom', default: param => defaultBasedOnParam(param),});Copy the code
  • selectorFamily()

  • Similar to Selector, but you can pass arguments to set and get properties.

const myNumberState = atom({ key: 'MyNumber', default: 2, }); const myMultipliedState = selectorFamily({ key: 'MyMultipliedNumber', get: (multiplier) => ({get}) => { return get(myNumberState) * multiplier; }, // optional set set: (multiplier) => ({set}, newValue) => { set(myNumberState, newValue / multiplier); }}); function MyComponent() { // defaults to 2 const number = useRecoilValue(myNumberState); // defaults to 200 const multipliedNumber = useRecoilValue(myMultipliedState(100)); return <div>... </div>; }Copy the code
  • The data query can then be performed by passing in the values it depends on.

Compare with the Hox state management library

  1. Compared to HOX:
    1. Recoi by Facebook 1. From Facebook’s official experimental project, still observable. 2. There are many apis.
    2. Hox is maintained by 1. Ant Financial and is in a relatively stable state. 2. Fewer apis.

Conclusion:

Recoil extracts the state from the application to form a state tree that communicates with the component via selector. It is positively interlaced with the components in the App. Pros: Recoil uses the React state in the background. Hooks are fully supported for use. The future will be a state management framework to look forward to.

References:

  1. Recoil document
  2. Recoil
  3. You Might Not Need Redux
  4. YouTube-Recoil
  5. Mobx Chinese documentation
  6. Take you inside Mobx
  7. Do you need Mobx or Redux?

❤️ Thank you

That is all the content of this sharing. I hope it will help you

Don’t forget to share, like and bookmark your favorite things.

Welcome to pay attention to the public number ELab team receiving factory good article ~