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
useRecoilValue
Reading synchronization statususeRecoilValueLoadable
Read 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
useSetRecoilState
Write 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
useResetRecoilState
Reset the status to default
Usage:
// An operational hook useResetRecoilState(todoListState)Copy the code
Read and write Recoil state
useRecoilState
The 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
useRecoilStateLoadable
Asynchronous 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
useRecoilCallback
Read 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