The Structure of the Fiber object is very complex. Not only are there many properties, but some properties are very complex objects. This feeling was particularly strong when I was learning hooks. MemoizedState and Fiber.updatequeue are two important properties of fiber. MemoizedState and fiber.updatequeue are two important properties of fiber.
Let’s start with class components
Let’s start with class components. MemoizedState is the state object defined by us. The structure of updateQueue is as follows:
fiber.updateQueue {
baseState,
firstBaseUpdate,
lastBaseUpdate,
shared: {
pending,
},
effects
}
Copy the code
First, the first three properties, baseState, firstBaseUpdate, and lastBaseUpdate, are related to interruptible updates. In concurrent mode, current updates may be interrupted by higher-priority updates, and baseState records the calculated states before the skipped update node. FirstBaseState and lastBaseUpdate form a linked list. Record all updates that were skipped due to lack of priority. Shared. pending is the actual list of all update objects, which is a circular list. Shared. pending refers to the last update object in the list. Effects is an array that stores the callback function for setState. For more details on how state is calculated for class components, see my article, which focuses on data structures.
Look at the function component
If you know how it works, react uses a linked list to store all the hooks used in the function component, and this list is stored in Fiber. MemoizedState. Each entry in this hooks list is a hook object. The structure of a hook object is as follows
hook {
memoizedState,
baseState,
baseQueue,
queue,
next
}
Copy the code
MemoizedState: Fiber. MemoizedState: Fiber. MemoizedState: Fiber.
No matter what kind of hook, this data structure will be used, and some hooks may only use a few of the properties. In the following, we will introduce the properties used by different hooks from simplicity to complexity.
useCallback, useMemo, useRef
Only hook. MemoizedState is used in these three hooks.
While useCallback accepts an array of functions and dependencies that need to be cached, hooke. memoizedState holds an array of cache functions and dependencies
function mountCallback(callback, deps) {
var hook = mountWorkInProgressHook();
var nextDeps = deps === undefined ? null : deps;
hook.memoizedState = [callback, nextDeps];
return callback;
}
Copy the code
UseMemo is basically the same as useCallback, except that useMemo holds the return value of the function
function mountMemo(nextCreate, deps) {
var hook = mountWorkInProgressHook();
var nextDeps = deps === undefined ? null : deps;
var nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
Copy the code
As for useRef, it’s easy to save an object
function mountRef(initialValue) {
var hook = mountWorkInProgressHook();
var ref = {
current: initialValue
};
{
Object.seal(ref);
}
hook.memoizedState = ref;
return ref;
}
Copy the code
The ref Object is closed using object.seal, which is freely configurable and cannot be configured on top of the object.preventExtensions.
useState
After looking at the three simple hooks, now make it a little more difficult and look at useState.
UseState is more complex, using all the properties on the hook object
Hook {memoizedState: saved state, baseState: same as updateQueue. BaseState: And class components updateQueue. FirstBaseUpdate and lastBaseUpdate effect is the same, the queue: {pending: save the update object of circular linked list, dispatch: LastRenderedReducer: The reducer used in the last update; lastRenderedState: the reducer used in the last update}}Copy the code
Need to pay attention to some here, useState used to hook. The queue this property, and hook. The queue. The pending and class components updateQueue. Shared. Pending is similar, are used to store the update object. When using useState, fiber.updatequeue is null, which means that unlike the class component, useState does not use updateQueue, which can be confusing.
useEffect
Let’s take a look at what I personally think is the most complicated hook: useEffect. It’s complicated because useEffect’s data structure is convoluted. Let’s take a look at it
First of all, of the several properties on the hook object, useEffect is only used in one, that is, memoizedState, and an effect object is saved on memoizedState
hook {
memoized: {
tag: used to distinguish useEffect from useLayoutEffect,create: is the useEffect callback function,destory: return value of useEffect callback,deps: depends on an array,next: the next effect object, which forms a linked list},baseState: null.baseQueue: null.queue: null
}
Copy the code
In addition, useEffect uses fiber.updatequeue, which has a different structure than the class component
// updateQueue for the class component
{
baseState,
firstBaseUpdate,
lastBaseUpdate,
shared: {
pending
},
effects
}
/ / useEffect updateQueue
{
lastEffect
}
Copy the code
UseEffect places the Effect object not only on hook.memoized, but also on fiber.updatequeue, both of which are circular linked lists
An 🌰
Now, if you’re a little confused, let’s take a real example
const App = () = > {
const [num, setNum] = useState(1)
const handleClick1 = useCallback(() = > {
setNum((pre) = > pre + 1)}, [])const [count, setCount] = useState(1)
const handleClick2 = useCallback(() = > {
setCount((pre) = > pre + 1)
}, [])
useEffect(() = > {
console.log('1st effect', num)
}, [num])
useEffect(() = > {
console.log('2nd effect', num)
}, [num])
useLayoutEffect(() = > {
console.log('use layout', count)
}, [count])
return (
<div>
<p>{num}</p>
<p>{count}</p>
<button onClick={handleClick1}>click me</button>
<button onClick={handleClick2}>click me</button>
</div>)}Copy the code
In the example above, we use two Usestates, and each event handler is wrapped in useCallback, as well as useEffect and useLayoutEffect, which we’ll take a look at below
- First of all, execution
useState
, so infiber.memoizedState
One is mounted on ituseState
thehook
object
- And then we go to
useCallback
In thehooks
Add one more item to the list
- And then again
useState
anduseCallback
, so it is the same as the previous two steps
- And then the more complicated
useEffect
Now,useEffect
Not only does it addhook
Object tohooks
In the linked list, it will changeupdateQueue
- There’s another one in the back
useEffect
This step is more complicated
As you can see, when you use useEffect, not only do you create a linked list of hooks, but you also create a circular list of effect objects
- And then it executes
useLayoutEffect
Finally, let’s summarize this example