React's hooks are a feature that appeared after fiber, so many people mistakenly believe that hooks must rely on fiber to be implemented. In fact, this is not the case. The two are not necessarily connected.
Now, hooks are not only implemented in react, but also in frameworks such as preact, react ssr, and midway. Their implementation does not rely on fiber.
Let's take a look at how hooks in these different frameworks are implemented:
React describes the interface through jsx, which will be compiled into a render function by compilation tools such as babel or tsc, and then executed to generate vdom:
The render function here was React.createElement before React17:
After React 17, it was changed to jsx:
This jsx-runtime will be automatically introduced, and there is no need to retain a React import for each component as before.
Render function execution generates vdom:
The structure of vdom is like this:
Before React16, this vdom would be rendered recursively, adding, deleting, and modifying the real dom.
After React16 introduced the fiber architecture, there was an extra step: first convert vdom into fiber, and then render the fiber.
The process of converting vdom to fiber is called reconcile, and the final process of adding, deleting and modifying the real dom is called commit.
Why do we need to make such a conversion?
Because vdom only has references to the child node children, and no references to the parent node parent and other sibling nodes, this results in the need to recursively render all vdom nodes to the dom at one time without interrupting.
What happens if it's interrupted? Because the parent node and sibling nodes are not recorded, we can only continue to process the child nodes, but cannot process other parts of the vdom.
That's why React introduced this kind of fiber structure, which has references such as parent node return, child node child, sibling node sibling, etc., which can be interrupted, because all unprocessed nodes can be found after interruption and recovery.
The structure of the fiber node is as follows:
This process can be interrupted, and naturally it can be scheduled, which is a schdule process.
Therefore, the fiber architecture is divided into three stages: schdule, reconcile (convert vdom to fiber), and commit (update to dom).
Hooks can be used in function components to access some values, and these values are stored on the fiber node.
For example, 6 hooks are used in this function component:
Then there is a memorizedState linked list of 6 elements on the corresponding fiber node:
Concatenated by next:
Different hooks access values on different elements of the memorizedState linked list. This is the principle of react hooks.
This linked list has a creation phase and an update phase, so you will find that the final implementation of useXxx is divided into mountXxx and updateXxx:
The mount phase here is to create hook nodes and assemble them into a linked list:
The created hook linked list will be linked to the memorizedState attribute of the fiber node.
When updating, you can naturally retrieve this hook list from the fiber node:
In this way, in multiple renderings, the useXxx api can find the corresponding memorizedState on the fiber node.
This is the principle of react hooks. You can see that it stores the hook on the fiber node.
So what's the difference with preact?
Preact is a more lightweight framework compatible with react code. It supports class components and function components, as well as react features such as hooks. However, it does not implement the fiber architecture.
Because it mainly considers the ultimate size (only 3kb), not the ultimate performance.
We just learned that react stores the hook list on the fiber node. If preact does not have a fiber node, where will the hook list be stored?
In fact, it is easy to think that fiber only modifies vdom to improve performance, and there is no essential difference from vdom. Then can we just store the hook on vdom?
Indeed, preact places the hook list on vdom.
For example, this function component has 4 hooks:
Its implementation is to access the corresponding hook on vdom:
It does not divide the hook into two stages, mount and update, like react, but merges them together for processing.
As shown in the figure, it stores hooks in the array of component.__hooks and accesses them through subscripts.
This component is an attribute on vdom:
That is, the value of hooks is stored in the array of vnode._component._hooks.
Compare the differences between react and preact in implementing hooks:
in react, the hook list is stored on the fiberNode.memorizedState attribute, in preact, the hook list is stored on the vnode._component._hooks attribute.
The hook list in react is concatenated through next, and in preact
React separates the creation and update of the hook linked list
through subscript access
Therefore, the implementation of hooks is not Relying on fiber, it just needs to find a place to store the hook data corresponding to the component. As long as it can be retrieved during rendering, it doesn't matter where it is stored.
Because vdom, fiber and component rendering are strongly related, they are stored in these structures.
For example, when react ssr implements hooks, it neither exists on fiber nor on vdom:
In fact, in addition to csr, the react-dom package can also do ssr:
Use the render method of react-dom when csr:
When ssr, use the renderToString method or renderToStream method of react-dom/server:
Do you think vdom to fiber conversion will be done during ssr?
Definitely not. Fiber is a structure introduced to improve the rendering performance when running in the browser, make calculations interruptible, and perform calculations when idle.
Server-side rendering naturally does not require fiber.
If fiber is not needed, where does it store the hook list? vdom?
It can indeed be placed in vdom, but it is not.
For example, useRef hooks:
It is a linked list concatenated with next starting from firstWorkInProgressHook.
And firstWorkInProgressHook is the first hook node created with createHook:
It is not mounted on vdom.
Why?
Because ssr only needs to be rendered once and does not need to be updated, there is no need to hang it on vdom.
Just clear the hook list every time you finish processing the hooks of each component:
Therefore, when using react ssr, hooks exist in global variables.
Compare the difference in the implementation principles of hooks in react csr and ssr:
in csr, fiber will be created from vdom, which is used to make rendering interruptible and improve performance through idle scheduling, but in ssr, it will not be rendered directly by vdom.
using
csr, hooks are saved to the fiber node, while when using ssr, they are directly placed on global variables, and are cleared after each component is processed. Because CSR will not be used a second time
, the creation and update of hooks will be divided into two phases: mount and update, while SSR will only be processed once, and only the creation phase.
The implementation principle of hooks is actually not complicated, that is, in a certain context Store a linked list in the linked list, and then the hooks api accesses the corresponding data from different elements of the linked list to complete their respective logic. This context can be vdom, fiber or even a global variable.
However, the idea of hooks is still very popular. The server-side framework midway produced by Taobao has introduced the idea of hooks:
Midway is a Node.js framework:
The server-side framework naturally does not have structures such as vdom and fiber, but the idea of hooks does not rely on these. To implement the hooks API, you only need to place a linked list in a certain context.
Midway implements an API similar to react hooks:
I haven't looked specifically where the hook list exists, but we have already mastered the implementation principle of hooks. As long as there is a context to store the hook list, it can be anywhere.
react hooks are a feature that emerged after the react fiber architecture. Many people mistakenly believe that hooks must be implemented with fiber. We looked at the implementation of hooks in react, preact, react ssr, and midway respectively, and found that this is not the case:
it will be fine. So, do react hooks have to rely on fiber to implement it?
Obviously not, it can be used with fiber, vdom, global variables, or even any context.