Hooks are fundamentally a simple way to encapsulate state and code logic. Originally introduced in React, it has been adopted by other frameworks such as Vue, Svelte, and even packaged as a pure JS library. The JS design pattern for hooks requires a good understanding of closures in javascript.
This article will simply implement React Hook.
closure
The purpose of using hooks in React is to completely avoid the complexity of classes and higher-order components. Hooks involve two things to understand, the execution context and the closure (you no longer need to use “this”).
A closure is simply a function that can access variables outside of that function.
Closure is when a function is able to remember and access its lexical scope even when that function is executing outside its lexical scope.
useState
Here’s a good example:
In the code above, we create a simplified version of React, useState, with two internal functions, state and setState. State returns the local variable _val defined above, and setState sets the local variable.
But the state implemented here is a getter function, which is not ideal and will be changed a little later. Importantly, with foo and setFoo, we can access and manipulate the internal variable _val, a reference called a closure.
Usage in function components
Here is a counter component:
Instead of rendering the Dom, use render to simulate the react rendering process.
In the above example, we exported state as a function, not a variable, but if we exported _val directly, there would be a bug:
This is one of the problems with the state closure. When we deconstruct foo from the output of useState, it references _val from the original useState call and does not change.
The Module Pattern
Using closures in the module pattern solves this problem.
To put it simply, move the closure to a module-scoped closure:
This allows us to track component state, and useState exports a variable that correctly assigns the internal _val value each time the component renders:
The Render method simply simulates the Render process of React, requiring render every time a state update is made (a feature of React is to update the Dom once). React does this for us when we actually use it.
useEffect
Unlike useState, useEffect is executed asynchronously. But the implementation is similar:
Usag sanctioned:
UseEffect is reexecuted when the component is render, using _deps to determine if callback is required.
Not Magic, just Arrays
The useState and useEffect implementations above are approximate, so if multiple hooks are used, val or dePS will be overwritten (only one).
As described in React Hooks: Not Magic, Just Arrays, we can reuse hooks using arrays.
Use arrays to add val and DEps to the hook queue so that there is no overwriting problem.
[hooks[currentHook++], setState]; currentHook+ 1 only after export CurrentHook is 0, and the currrentHook is [hook[0],setState], after which currrentHook +1.
Therefore, the basic principle is a hook array and index.
Rules for using hooks
- Do not call Hooks in loops, conditions, or nested functions.
- Only the react function calls the hook.
The React Hook stack diagram describes the react Hook execution process
Use a stack diagram to describe the above process:
We can clearly see that the hook is an array, so when we use the hook in an if statement, if the condition is not true, the index will be distorted in the second rendering. Such as:
The real React implementation
(To be added)