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

  1. First of all, executionuseState, so infiber.memoizedStateOne is mounted on ituseStatethehookobject

  1. And then we go touseCallbackIn thehooksAdd one more item to the list

  1. And then againuseStateanduseCallback, so it is the same as the previous two steps

  1. And then the more complicateduseEffectNow,useEffectNot only does it addhookObject tohooksIn the linked list, it will changeupdateQueue

  1. There’s another one in the backuseEffectThis 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

  1. And then it executesuseLayoutEffect

Finally, let’s summarize this example