React source code

4. Start with Legacy or Concurrent (start at the entrance and let’s move on to the future)

Video lessons & Debug demos

The purpose of the video course is to quickly master the process of running the React source code and scheduler, Reconciler, Renderer, Fiber, etc., in react, and debug the source code and analysis in detail to make the process clearer.

Video course: Enter the course

Demos: demo

Course Structure:

  1. You’re still struggling with the React source code.
  2. The React Mental Model
  3. Fiber(I’m dom in memory)
  4. Start with Legacy or Concurrent (start at the entrance and let’s move on to the future)
  5. State update process (what’s going on in setState)
  6. Render phase (awesome, I have the skill to create Fiber)
  7. Commit phase (I heard renderer marked it for us, let’s map real nodes)
  8. Diff algorithm (Mom doesn’t worry about my Diff interviews anymore)
  9. Function Component saves state
  10. Scheduler&lane model (to see tasks are paused, continued, and queue-jumped)
  11. What is concurrent mode?
  12. Handwriting mini React (Short and Sharp is me)

In this tutorial, we looked at the react workflow and got a general idea of what each workflow does. Now let’s start with the Source code for each part of react.

React mode for entry functions

First, react has three modes of entry into the body function. We can compare the three modes from the Official React documentation using Concurrent mode (experimental)

  • Legacy mode: ReactDOM.render(<App />, rootNode). This is the way the React app is currently used. There are currently no plans to remove this schema, but it may not support these new features.
  • Blocking mode: ReactDOM.createBlockingRoot(rootNode).render(<App />). It is currently being tested. As the first step in the migration to Concurrent mode.
  • Concurrent mode: ReactDOM.createRoot(rootNode).render(<App />). React is currently being tested as the default development mode when it stabilizes. This mode is onAll of theNew features.

Feature comparison:

Legacy mode Blocking mode Concurrent mode
String Refs 🚫 * * 🚫 * *
Legacy Context 🚫 * * 🚫 * *
findDOMNode 🚫 * * 🚫 * *
Suspense
SuspenseList 🚫
Suspense SSR + Hydration 🚫
Progressive Hydration 🚫
Selective Hydration 🚫 🚫
Cooperative Multitasking 🚫 🚫
Automatic batching of multiple setStates 🚫 *
Priority-based Rendering 🚫 🚫
Interruptible Prerendering 🚫 🚫
useTransition 🚫 🚫
useDeferredValue 🚫 🚫
Suspense pass Reveal “Train” 🚫 🚫

* legacy mode has automatic batching in composited events, but is limited to one browser task. To use this function, non-React events must use unstable_batchedUpdates. In blocking and concurrent modes, all setStates are batch by default.

** : Alerts will be issued during development.

React runtime implications of different modes

Legacy mode is the one we commonly use, where the process of building the DOM is synchronous, so in the Reconciler’s Render Reconciler, if the Diff process is particularly time-consuming, the result is that js keeps blocking high-priority tasks (such as user click events), showing up as pages that are stalling and unresponsive.

Concurrent Mode is the future Mode of React, which uses timeslices to schedule asynchronous interruptible tasks. The timeslices vary in length according to device performance. In each timeslices, threads are actively transferred to high-priority tasks when the task expires. In chapter 11, scheduler&lane model, this part will explain how react implements asynchronous interruptible tasks and how tasks are prioritized and high-priority.

The order and purpose of function calls

  1. Main process:

  2. Main function execution process:

  • Watch the breakpoint debugging video for more details on function calls:

  • To see how this works, use demo_0:

    Legacy mode:

    • Render call legacyRenderSubtreeIntoContainer, CreateRootImpl calls createFiberRoot to create a fiberRootNode, and createHostRootFiber to create a rootFiber, where fiberRootNode is the root of the project. RootFiber is the node on which the current application hangs, which is the root node after the reactdom.render call
    // The topmost node is the fiberRootNode, the root node of the entire project
    ReactDOM.render(<App />.document.getElementById("root"));//rootFiber
    ReactDOM.render(<App />.document.getElementById("root"));//rootFiber
    Copy the code

    • After creating the Fiber node, called legacyRenderSubtreeIntoContainer updateContainer create create the Update object mounted on the updateQueue circular linked list, Then scheduleUpdateOnFiber calls performSyncWorkOnRoot to enter the Render and Commit phases

    Concurrent mode:

    • CreateRoot calls createRootImpl to create fiberRootNode and rootNode
    • After creating the Fiber node, called ReactDOMRoot. Prototype. Render updateContainer, Then scheduleUpdateOnFiber performConcurrentWorkOnRoot entered the render phase asynchronous scheduling and the commit phase

  1. Legacy mode main function notes:
function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
  / /...
  var root = container._reactRootContainer;
  var fiberRoot;

  if(! root) {/ / the mount
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);Create a root node
    fiberRoot = root._internalRoot;

    if (typeof callback === 'function') {// Handle the callback
      var originalCallback = callback;

      callback = function () {
        var instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    } 


    unbatchedUpdates(function () {
      updateContainer(children, fiberRoot, parentComponent, callback);// Create the update entry
    });
  } else {
    / / update
    fiberRoot = root._internalRoot;

    if (typeof callback === 'function') {// Handle the callback
      var _originalCallback = callback;

      callback = function () {
        varinstance = getPublicRootInstance(fiberRoot); _originalCallback.call(instance); }; } updateContainer(children, fiberRoot, parentComponent, callback); }}Copy the code
function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) {
  var root = new FiberRootNode(containerInfo, tag, hydrate);/ / create fiberRootNode
  const uninitializedFiber = createHostRootFiber(tag);/ / create rootFiber
  //rootFiber connects to fiberRootNode
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;
  / / create updateQueue
  initializeUpdateQueue(uninitializedFiber);
  return root;
}

// initializeUpdateQueue is used for HostRoot or ClassComponent to create updateQueue and then mount updateQueue to fiber node
export function initializeUpdateQueue<State> (fiber: Fiber) :void {
  const queue: UpdateQueue<State> = {
    baseState: fiber.memoizedState,// The initial state, based on which the new state will be calculated according to Update
    firstBaseUpdate: null.//Update the header of the linked list
    lastBaseUpdate: null.//Update the end of the list
		// New updates are stored on shared.pending as a one-way loop, which will be clipped when state is calculated and joined after //lastBaseUpdate
    shared: {
      pending: null,},effects: null}; fiber.updateQueue = queue; }Copy the code
function updateContainer(element, container, parentComponent, callback) {
  var lane = requestUpdateLane(current$1);// Getting the currently available lanes is explained in Chapter 12
  var update = createUpdate(eventTime, lane); / / create the update

  update.payload = {
    element: element//jsx
  };

  enqueueUpdate(current$1, update);/ / update the team
  scheduleUpdateOnFiber(current$1, lane, eventTime);/ / schedule update
  return lane;
}
Copy the code
function scheduleUpdateOnFiber(fiber, lane, eventTime) {
  if (lane === SyncLane) {// Synchronize lane to Legacy mode
    / /...
    performSyncWorkOnRoot(root);// Render in chapter 6
  } else {/ / concurrent mode
    / /...
    ensureRootIsScheduled(root, eventTime);// Make sure root is scheduled}}Copy the code
  1. Concurrent main function annotation:
function ensureRootIsScheduled(root, currentTime) {
  / /...
  var nextLanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes); / / nextLanes calculation

  / /...

 // Convert lane's priority to Schduler's priority
  var schedulerPriorityLevel = lanePriorityToSchedulerPriority(newCallbackPriority);
  / / to schedulerPriorityLevel priority to execute performConcurrentWorkOnRoot is the starting point of the concurrent mode
  newCallbackNode = 	  scheduleCallback(schedulerPriorityLevel,performConcurrentWorkOnRoot.bind(null, root));
}
Copy the code
  1. Differences between the two modes:
    1. The second argument passed in createRootImpl is different: LegacyRoot and ConcurrentRoot
    2. The priority of the lane obtained in requestUpdateLane is different
    3. According to the different priorities in the function scheduleUpdateOnFiber into different branches, legacy mode into performSyncWorkOnRoot, concurrent asynchronous scheduling performConcurrentWorkOnRoot patterns