Hello, bytedance spring Dance has begun. Are you ready? Welcome everyone to look for me to push, or enter the team number to deliver a resume.
Author: Ran Wu
Next we invited bytedance data platform xx brought Recoil source code exploration, let us learn together, build up, is also a good state solution.
1. Introduction
Recoil is a facebook status solution with the following features: 1. High performance rendering, 2. Generate derived values from state.
Characteristics of 2.
High performance rendering
In the second child of List and Canvas, if there is state that needs to be shared, the common approach is to promote the state to the nearest common ancestor node, but the problem with this is that it will cause global rerendering if no additional processing is done. Of course, this can be optimized using useMemo + immutable. Another solution is to use Context. Using Context, if you want to solve the global rendering problem, you need to put the values that need to be shared in a separate Context. The problem with this is that on our components, if we have more state to share, we need to wrap a lot of providers around it. It’s also difficult to maintain.
Recoil introduced an entirely new concept, Atom, which atomizes and decentralizes state management. Atom can be updated and subscribed to by components, and as soon as Atom is updated, the components subscribed to Atom can be precisely rerendered.
The derived value
From Atom, you can generate new data that a component can subscribe to, similar to Vue’s calculated properties.
Even more powerful here is the ability to handle asynchronous requests in the GET method.
3. Core process source code
Initialize the
Similar to Redux, when using global data, you need to wrap a component on top of the root component, or in Recoil, you need to wrap the cocomponent RecoilRoot.
For RecoilRoot, we wrap a Context layer on top of our business code, which provides methods to get data from a store, update data from a store, and so on.
Atomic state definition
In Recoil, each of the smallest units of state (that is, cannot be calculated from other states) is defined as an Atom. For each Atom, you return an object that contains the key (which is deconstructed from the passed option and needs to be globally unique) and provides methods like GET and set.
For Atom, you have to have a set method, because we define an atomic data and we definitely need to modify it, otherwise it doesn’t make sense. Atom, on the other hand, has a relatively simple get method that, in the case of a key, can be retrieved directly from a Map of states.
Derived value definition
For selectors, as with Atom, get and other methods are provided, but for selectors, Atom is available, so the set method is not required.
The get method is also different from the Atom method, because the selector get has a cache. If there is no cache, the selector get will calculate the corresponding value based on the dependent Atom, and the value will be cached, and the next fetch, if the value of the dependent has not changed, can be evaluated from the cache. Improve performance as much as possible.
Subscribe/update shared values in the component
In Recoil, the common hooks to subscribe/update are useRecoilState, useRecoilValue, useSetRecoilState. UseRecoilState contains the other two.
Subscribe to the value
The main call chain in useRecoilValue is as follows:
useRecoilValue -> useRecoilValueLoadable -> useRecoilValueLoadable_LEGACY -> subscribeToRecoilValue
Copy the code
In this case, using Atom or selector in the component via useRecoilValue(useRecoilState) adds a subscription to the component. This subscription is implemented via useState. When the corresponding RecoilValue is updated, Rerender the component to update the component with minimal granularity. Here is very important, storeState. NodeToComponentSubscriptions. Set method, the method in the global store updated globally unique key on the maintenance subscription map, with this map, When the values are updated, the exact relationship can be found, triggering forceUpdate to rerender the component.
Update the value
The main call chain in useSetRecoilState is as follows:
useSetRecoilState -> setRecoilValue -> queueOrPerformStateUpdate -> applyActionsToStore -> store.replaceState -> Batcher Effect -> sendEndOfBatchNotificationsCopy the code
The store. ReplaceState method is set when the store is initialized in RecoilRoot.
The notifyBatcherOfChange. Current is in < Batcher > component by useState to state, in the form of updates, triggering useEffect, Perform sendEndOfBatchNotifications
The callback, which is the subscription that was registered when the value was subscribed above, is executed to precisely update the corresponding component.
The important thing to note here is that you cannot use useRecoilState if the component only needs to update the state of the share, because using useRecoilState will also subscribe to the state of the share, resulting in unnecessary rendering.
4. To summarize
From Recoil’s source code, it ensures minimal granularity of rendering by separating subscriptions and updates, minimizes the number of units of data, and makes use of derived values generated by selectors for caching, improving computational efficiency.