react source code analysis 6.legacy mode and concurrent mode

Posted by neogemima on Wed, 08 Dec 2021 00:29:23 +0100

react source code analysis 6.legacy mode and concurrent mode

Video Explanation (efficient learning): Enter learning

Previous articles:

1. Introduction and interview questions

2. Design concept of react

3.react source code architecture

4. Source directory structure and debugging

5. JSX & Core api

6.legacy and concurrent mode entry functions

7.Fiber architecture

8.render stage

9.diff algorithm

10.commit phase

11. Life cycle

12. Status update process

13.hooks source code

14. Handwritten hooks

15.scheduler&Lane

16.concurrent mode

17.context

18 event system

19. Handwritten Mini react

20. Summary & answers to interview questions in Chapter 1

react start mode

React has three modes to enter the entry of the main function, which can be accessed from the official document of react Use Concurrent mode (experimental) Three modes are compared:

  • legacy mode: reactdom.render (< app / >, RootNode). This is how the React app is currently used. There is no plan to delete this mode, but this mode may not support these new functions.
  • blocking mode: reactdom. Createblockingroot (RootNode). Render (< app / >). At present, the experiment is under way. As the first step in migrating to concurrent mode.
  • concurrent mode: reactdom.createroot (RootNode). Render (< app / >). At present, in the experiment, it is intended to be the default development mode of React after it is stable in the future. This mode opens all new functions.

Characteristic comparison:

legacy mode has the function of automatic batch processing in composite events, but it is limited to one browser task. Nonstable must be used for non React events to use this function_ batchedUpdates. In blocking mode and concurrent mode, all setstates are batch processed by default. A warning will be issued during development

The meaning of different modes in react runtime

legacy mode is commonly used by us. The process of building dom is synchronous. Therefore, in render's reconciler, if the diff process is particularly time-consuming, the result is that js always blocks high priority tasks (such as user click events), which shows that the page is stuck and cannot respond.

concurrent Mode is the future mode of react. It uses time slice scheduling to realize asynchronous and interruptible tasks. According to different device performance, the length of time slice is also different. In each time slice, if the task reaches the expiration time, it will actively give up the thread to the high priority task. This section will the scheduler & lane model in section 15.

The main execution process of the two mode functions

1. Main execution process:

2. Detailed function calling process:

Use demo_0 is clearer with the video debugging. The yellow part is the main task to create fiberRootNode and rootFiber, the red part is to create Update, and the blue part is the entry function of the scheduling render phase

3.legacy mode:

  • Render calls legacyRenderSubtreeIntoContainer, and finally createRootImpl calls to createFiberRoot to create fiberRootNode, then calls createHostRootFiber to create rootFiber, where fiberRootNode is the root node of the whole project. RootFiber is the node node in the current application, that is, the root node after the ReactDOM.render is invoked.

    //The top-level node is the root node of the entire project, fiberRootNode
    ReactDOM.render(<App />, document.getElementById("root"));//rootFiber
    ReactDOM.render(<App />, document.getElementById("root"));//rootFiber

  • After creating the Fiber node, legacyRenderSubtreeIntoContainer calls updateContainer to create an Update object and mount it on the ring linked list of updateQueue. Then, execute scheduleUpdateOnFiber and call performSyncWorkOnRoot to enter the render stage and commit stage

4.concurrent mode:

  • createRoot calls createRootImpl to create fiberRootNode and rootNode
  • After creating the Fiber node, call ReactDOMRoot.prototype.render to execute updateContainer, then scheduleUpdateOnFiber asynchronously dispatches performConcurrentWorkOnRoot into the render stage and commit stage.

5. Comments on main functions of legacy mode

function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
  //...
  var root = container._reactRootContainer;
  var fiberRoot;

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

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

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


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

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

      callback = function () {
        var instance = getPublicRootInstance(fiberRoot);

        _originalCallback.call(instance);
      };
    } 
    
    updateContainer(children, fiberRoot, parentComponent, callback);
  }
}
function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) {
  var root = new FiberRootNode(containerInfo, tag, hydrate);//Create fiberRootNode
  const uninitializedFiber = createHostRootFiber(tag);//Create rootFiber
  //rootFiber and fiberRootNode connections
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;
  //Create updateQueue
  initializeUpdateQueue(uninitializedFiber);
  return root;
}

//For HostRoot or ClassComponent, an updateQueue is created using initializeUpdateQueue, and then the updateQueue is mounted on the fiber node
export function initializeUpdateQueue<State>(fiber: Fiber): void {
  const queue: UpdateQueue<State> = {
    baseState: fiber.memoizedState,//The initial state. Based on this state, the new state will be calculated according to Update
    firstBaseUpdate: null,//The header of the linked list formed by Update
    lastBaseUpdate: null,//The tail of the linked list formed by Update
        //The newly generated update will be saved on shared.pending as a one-way circular linked list. When calculating the state, the circular linked list will be cut off and connected after / / lastBaseUpdate
    shared: {
      pending: null,
    },
    effects: null,
  };
  fiber.updateQueue = queue;
}
function updateContainer(element, container, parentComponent, callback) {
  var lane = requestUpdateLane(current$1);//Get the currently available lane and explain it in Chapter 12
  var update = createUpdate(eventTime, lane); //Create update

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

  enqueueUpdate(current$1, update);//update join the team
  scheduleUpdateOnFiber(current$1, lane, eventTime);//Schedule update
  return lane;
}
function scheduleUpdateOnFiber(fiber, lane, eventTime) {
  if (lane === SyncLane) {//Synchronous lane corresponding legacy mode
    //...
    performSyncWorkOnRoot(root);//The starting point of the render phase is explained in Chapter 6
  } else {//concurrent mode
    //...
    ensureRootIsScheduled(root, eventTime);//Ensure that root is scheduled
  } 
}

6. Notes to concurrent main functions:

function ensureRootIsScheduled(root, currentTime) {
  //...
  var nextLanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes); //Calculate nextLanes

  //...

 //Convert the priority of lane to that of Schuller
  var schedulerPriorityLevel = lanePriorityToSchedulerPriority(newCallbackPriority);
  //Execute performcurrentworkonroot with the priority of schedulerPriorityLevel, which is the starting point of concurrent mode
  newCallbackNode =       scheduleCallback(schedulerPriorityLevel,performConcurrentWorkOnRoot.bind(null, root));
}

7. Differences between the two modes:

  1. The second parameter passed in createRootImpl is different. One is LegacyRoot and the other is ConcurrentRoot
  2. The priority of the lane obtained in requestUpdateLane is different
  3. In the function scheduleUpdateOnFiber, different branches are entered according to different priorities. The legacy mode enters performSyncWorkOnRoot, and the concurrent mode will asynchronously schedule performcurrentworkonroot

Topics: React