If you don’t know anything about Recoil, it’s best to check out the official documentation. This article will cover the use of common apis.

Recoil is facebook’s official state management initiative, and react works well together.

The core concept

Using Recoil, you can create a data flow diagram from atoms (shared states) all the way through selectors (pure functions) to the React component. Atoms are units of state for which components can be subscribed. Selectors can convert this state synchronously or asynchronously.

atoms

Atoms are units of state that are updatable and subscriptable, and when the atoms are updated, the components of each subscription are re-rendered with new values.

They can also be created at run time, using Atoms instead of React local component state. If multiple components use the same atom, then all of them share their state.

// Const listState = atom({key:'list', default:[]})Copy the code

Atoms require a unique key, ++ which can be used for debugging, persistence, and some high-level apis that allow you to view the diagrams of all Atoms ++. Make sure keys are globally unique; like React component states, they also have default values

Reading Atom in the component can be read using useRecoilValue. Read and write using useRecoilState.

Function App(){// Get atom const list = useRecoilValue(listState); // Get and set const [list,setList] = useRecoilState(listState)}Copy the code

Meanwhile,default is either a fixed value, an asynchronous Promise, or another Atom

function atom<T>({ key: string, default: T | Promise<T> | RecoilValue<T>, dangerouslyAllowMutability? : Boolean,}): RecoilState<T> // Const listState = atom({key: 'list', default: (async () => { const res = await fetch('http://api.manster.me/getCampaignData').then(res => res.json()); return res; }) ()}); // Other atom const aState = atom({key:'a', default:'aaa'}) const bState = atom({key:'b', default:aState})Copy the code

In general, you need to use the following hooks with Atom.

  • useRecoilState()Use this hook when you need to read and write atom at the same time. Using this hook causes the component to subscribe to Atom.
  • useRecoilValue()Use this hook when you only need to read Atom. Using this hook causes the component to subscribe to Atom.
  • useSetRecoilState()Use this hook when you only need to write Atom.
  • useResetRecoilState(): Use this hook when you need to reset Atom to the default value.

selector

Official definition: A selector is a function or a derived state that represents Recoil. You can think of them as “pure functions” without side effects that always return the same value for a given set of dependency values. If only get is provided, the selector is read-only and a RecoilValueReadOnly object is returned. If a set is also provided, it returns a writable RecoilState object

Function selector<T>({key: string, // only key get: ({get: GetRecoilValue gets the value from another Atom or selector, and any Atom or selector passed into the function is implicitly added to a list of dependencies for that selector. If any of the selector's dependencies change, the selector recalculates the value. }) => T | Promise<T> | RecoilValue<T>, set? : ({get: GetRecoilValue, // a function used to get a value from another Atom or selector. This function [does not] subscribe to a given Atom or selector for a selector. Set: SetRecoilState, // a function used to set Recoil's state. The first parameter is Recoil's state, and the second parameter is the new value. The newValue can be an update function or an object of type DefaultValue that passes the update operation reset: ResetRecoilState,}, newValue: T | DefaultValue, ) => void, dangerouslyAllowMutability? : boolean, })Copy the code

Specific method of use

const aState = atom({ key: 'a', default: 'a', }); const bState = atom({ key: 'b', default: 'b', }); const gsSelector = selector({ key: 'getandset', get: ({ get }) => { let a = get(aState); let b = get(bState); return a + b; }, set: ({get, set}, newValue) => {// this get will not do dependency collection const c = get(cState); set(bState, newValue); }}); function Test() { const [gs, setgs] = useRecoilState(gsSelector); // Like atom, can be written directly; const add = ()=>{ setgs('+1') } console.log('gs', gs); return <div> <Button onClick={add}>add</Button> </div>; }Copy the code

Asynchrony can be written this way

const myQuery = selector({ key: 'MyDBQuery', get: async () => { const response = await fetch(getMyRequestUrl()); return response.json(); }});Copy the code

Core API

RecoilRoot

Usage:

<RecoilRoot>
  <App/>
</RecoilRoot>

Copy the code

Recoil context, there can be multiple. The key in Atom and selector is globally unique to RecoilRoot. Multiple nested RecoilRoot inner layers overwrite outer ones.

The atom, the selector

Usage: All described above

Atom stands for Recoil State. The initial value for atom is set in the default argument, which can be a certain value, a Promise, another Atom, or a selector.

Selector is a pure function that evaluates and caches derived data from Recoil state. A selector is Recoil derived state, and it can derive data not only from Atom, but also from other selectors. The callback to get, which takes atom or selector, becomes a dependency on the selector. When the dependency changes, the selector recalculates the new derived data and updates the cache. Cache values are used if the dependency does not change. The return value of the selector is returned by the get method, and it can be a calculated, deterministic value, or it can be a Promise, or another selector, or atom, or if atom or selector is a Promise, then the state value is taken asynchronously, Recoil provides a dedicated API for getting asynchronous status and supports using React Suspense to show asynchronous status

Set and get the Recoil State hooks API:

Read the Recoil state

  • useRecoilValueReading synchronization status
  • useRecoilValueLoadableRead asynchronous states, including hasValue, Loading, and hasError, and return a Loadable object with the following fields

State: Indicates the state of the selector. The optional values are ‘hasValue’, ‘hasError’, ‘loading’. Contents: This value represents the result of Loadable. If the state is hasValue, the value is the actual result; If the state is hasError, an error object is thrown; If the state is loading, the value is Promise.

Usage:

Const listState = atom({key: 'list', default:) const listState = atom({key: 'list', default: (async () => { const res = await fetch('http://api.manster.me/getCampaignData').then(res => res.json()); return res; }) ()}); const listLoadable = useRecoilValueLoadable(listState); switch (listLoadable.state) { case 'hasValue': return <div>... something... </div>; case 'loading': return <div>Loading... </div>; case 'hasError': throw listLoadable.msg; } // Use useRecoilValueLoadable instead if you don't use React Suspense.Copy the code

Write a Recoil state

  • useSetRecoilStateWrite state, which returns a setter function that updates the Recoil writable state value, and a setter function that can be used to change state asynchronously. You can pass this setter function a new value, or you can pass an update function that takes the last value as its argument.

usage

const setA = useSetRecoilState(aState);

setA('newValue')

setA((preState)=>{
  return preState+'new'
});


Copy the code
  • useResetRecoilStateReset the status to default

Usage:

// An operational hook useResetRecoilState(todoListState)Copy the code

Read and write Recoil state

  • useRecoilStateThe state of reading and writing

Usage:

  const [tempF, setTempF] = useRecoilState(tempFahrenheit);
  const [tempC, setTempC] = useRecoilState(tempCelcius);
 
  setTempC('newValue');
  setTempF((preValue)=>{
    return newValue  
  });
Copy the code
  • useRecoilStateLoadableAsynchronous read/write state

Usage:

Const [userNameLoadable, setUserName] = useRecoilStateLoadable(userNameState); // Const [userNameLoadable, setUserName] = useRecoilStateLoadable(userNameState); switch (userNameLoadable.state) { case 'hasValue': return <div>{userNameLoadable.contents}</div>; case 'loading': return <div>Loading... </div>; case 'hasError': throw userNameLoadable.contents; } setUserName('newValue') setUserName((pre)=>{ return 'newValue' })Copy the code
  • useRecoilCallbackRead and write state without updating the component

Usage:

// Can be used to preload data, but not refresh components, // For performance reasons, you might want to get the data before rendering so that the query can be made when we start rendering. The React documentation gives examples of how this pattern works for Recoil as well. Let's change the example above so that when the user clicks on the change user button, it starts getting the next user information: function CurrentUserInfo() { const currentUser = useRecoilValue(currentUserInfoQuery); const friends = useRecoilValue(friendsInfoQuery); const changeUser = useRecoilCallback(({snapshot, set}) => userID => { snapshot.getLoadable(userInfoQuery(userID)); // pre-fetch user info set(currentUserIDState, userID); // change current user to start new render }); return ( <div> <h1>{currentUser.name}</h1> <ul> {friends.map(friend => <li key={friend.id} onClick={() => changeUser(friend.id)}> {friend.name} </li> )} </ul> </div> ); }Copy the code

Where useRecoilState = useRecoilValue + useSetRecoilState, the last two are to obtain Recoil state read-only state value and set state value.

UseResetRecoilState is used to reset RecoilState to its initial state, which is the default value.

The useRecoilStateLoadable command is used to obtain the asynchronous status and setting status. The useRecoilValueLoadable command is used to obtain the read-only asynchronous status

UseRecoilCallback: Each of the above apis allows the component to subscribe to state and update the component when state changes, but using useRecoilCallback does not update the component. UseRecoilCallback allows the component to get and set Recoil State without subscribing to Recoil State. That is, the component can get state without updating it, and the component does not need to update itself to get a new state. Defer reading state until we need it instead of when the component monted.

Utility class API

atomFamily

Same as Atom, but supports passing parameters

Const myAtomFamily = atomFamily({key: 'MyAtom', default: param => defaultBasedOnParam(param),});Copy the code

selectorFamily

It’s written the same way as selector, but it allows us to pass arguments

const myMultipliedState = selectorFamily({ key: 'MyMultipliedNumber', get: (multiplier) => ({get}) => { return get(myNumberState) * multiplier; }, // optional set set: (multiplier) => ({set}, newValue) => { set(myNumberState, newValue / multiplier); }});Copy the code

waitForAll

Concurrent API, parameters are collected by dependency. Execute only when all asynchrony returns. Returns the result directly, with no intermediate states. Similar to the Promise. All

Use: return after all returns

// Const fetchAll = selector({key: 'fetchAll', get: ({ get }) => { const list = get( waitForAll([confirmListSelector, offlineListSelector, ingListSelector, endListSelector]), ); return list; }});Copy the code

waitForAny

The concurrent API, if there is an asynchronous return available value, returns it directly, maybe later. It will be executed multiple times. Return a status manager

// Return [{state:'hasValue'}, {state:'loading'}] // Return [{state:'hasValue'}, {state:'hasValue'}]Copy the code

waitForNone

The state of each dependency is returned, so it is executed multiple times. Various loading and hasValue

Use the same method for each of the above three waits.

noWait

Selector API, which does the same thing as useRecoilValueLoadable, except that useRecoilValueLoadable is an hooks. MoWait is a selector helper.

The function is to get the current state of an asynchronous dependency loading or hasValue.

const noWaitSelector = selector({ key: 'noWaitSelector', get: ({ get }) => { const result = get(noWait(p1Selector)); return result; }});Copy the code