It’s not easy to understand React 16 code, but it’s not as difficult as you might think. You’ll always figure it out with perseverance. As the author of 16 puts it:

Some of this may not make sense on first reading. That’s okay, it will make more sense with time.


You may not understand it the first time you read it, but that’s okay, as time goes on, it will.

It took me more than 4 days to understand the stack relationship. However, there are still a lot of things I don’t understand, so I’ll save this for implementing React Fiber myself. I believe it won’t take long.



Core theory of Fiber architecture


First of all, React traverses the DOM in the form of recursion. The advantage of recursion is that the code is simple and easy to understand, but it also has a fatal defect: it is difficult to interrupt and recover. Therefore, when we Mount and render huge nodes, we are always so stuck:

When React recursion is too large, it freezes the entire UI

Note that React doesn’t mean it can’t be played with dependency checking like Vue. The Mobx library is a library that allows players to experience vUe-like actions. But for people who use React heavily, Mobx’s mechanics don’t work well for multiplayer development, which is why Redux is so popular:

  • React is a function, Redux is a function
  • What Redux’s constraints mean for multiplayer development projects

In order to maintain the advantages of functional programming, React uses user-mode scheduling to re-implement React, which is also known as Fiber. With Fiber architecture, React performance can reach unprecedented levels:

Theoretically beyond all current frameworks

To do this, the solution is:

  1. Use the performance optimizer requestIdleCallback to take advantage of the CPU time wasted per frame
  2. Re-implement the React tree stack formation and update stack, abandon the recursion method, and traverse the tree in the form of loop (while) and out of the stack and in the stack. Tree depth traversal there are many types of tree depth traversal, recursion is the best understood, least coded, but also the most out of control, while + stack loop implementation depth traversal, can be used for pause and restore, quite easily, in the new React source, explicitly use the while loop.
  3. Each and every virtual DOM node has been upgraded, adding Alternate, child, Sibling and return attributes. These attributes are used to stop and restore the diff and patch in the update stage to divide them into each small task, the whole tree, Organized as a linked list.
React 15 and React 16 tree structure

These are the core principles of React 16. Once we understand these core principles, building our own wheels has been reduced.


The React stack is cleaned

The React Fiber call stack can be used to write the code in the React Fiber code. The React fiber call stack can be used to write the code in the React Fiber code.

  • ReactDOM. Render: Not much
  • LegacyCreateRootFromDOMContainer: this function will be to create a root root container. The fiber and is a special object, the container is here let us go to the Mount point, at the end of this function:
function legacyCreateRootFromDOMContainer(container, forceHydrate) {

  // Legacy roots are not async by default.
  var isAsync = false;
  return new ReactRoot(container, isAsync, shouldHydrate);
}Copy the code

The Async mode is written to death and cannot be performed. If you want to play, you can manually change it to True.

  • ReactRoot is basically making a ReactRoot Fiber node, which is a special node, and it’s not clear why it’s done.
  • UpdateContainer: Got the context, didn’t it really help
  • ScheduleRootUpdate: When we call render and setState, we make React “update”. Whether you render for the first time or update for the second time, React unifies the first render and subsequent updates. In fact, this point has been the previous point of criticism…
Update /mount var update = {expirationTime = {expirationTime = {expirationTime = {expirationTime = {expirationTime = {expirationTime = {expirationTime = {expirationTime = {expirationTime =} {element: element} partialState: {element: element}, callback: callback, isReplace: false, isForced: false, capturedValue: null, next: null };Copy the code

  • InsertUpdateIntoFiber: This function does one thing, both setState and render, to create an update. This method puts the update into a queue, which is a linked list
  • ScheduleWorkImpl: performs updates to the virtual DOM (Fiber tree). ScheduleWork is its encapsulation method
  • RequestWork (root, expirationTime) : this function is not useful in synchronous mode. In asynchronous mode, this function will eventually call rIC scheduling function
  • CreateWorkInProgress: Because we are in synchronous mode, we go straight down to createWorkInProgress. WorkInProgress refers to a new tree that is used for updating and replaces the old tree as the Current tree.
  • Workloop: Bold, because we’ve finally come to an important function, and this function is what I call the big loop, where async or asynchronous branches
while(next ! == null){ next =performUnitOfWork(next) }Copy the code

  • PerformUnitOfWork: This function will loop back to the next node in the tree, the logic is complicated, what it does is to take the current node, initialize, construct Fiber message (Sibling return, etc.), and then return the word element of the node out, the word element as next continues to find the child element, step by step, iterate, Until the tree runs out
  • BeginWork: in the function above, this function is actually a switch function, its source code is as follows:
function beginWork(current, workInProgress, // This function is a switch tag. switch (workinProgress. tag) {case IndeterminateComponent: return mountIndeterminateComponent(current, workInProgress, renderExpirationTime); case FunctionalComponent: return updateFunctionalComponent(current, workInProgress); case ClassComponent: return updateClassComponent(current, workInProgress, renderExpirationTime); case HostRoot: return updateHostRoot(current, workInProgress, renderExpirationTime); case HostComponent: return updateHostComponent(current, workInProgress, renderExpirationTime); case HostText: return updateHostText(current, workInProgress); case CallHandlerPhase: // This is a restart. Reset the tag to the initial phase. workInProgress.tag = CallComponent; // Intentionally fall through since this is now the same. case CallComponent: return updateCallComponent(current, workInProgress, renderExpirationTime); case ReturnComponent: // A return component is just a placeholder, we can just run through the // next one immediately. return null; case HostPortal: return updatePortalComponent(current, workInProgress, renderExpirationTime); case ForwardRef: return updateForwardRef(current, workInProgress); case Fragment: return updateFragment(current, workInProgress); case Mode: return updateMode(current, workInProgress); case ContextProvider: return updateContextProvider(current, workInProgress, renderExpirationTime); case ContextConsumer: return updateContextConsumer(current, workInProgress, renderExpirationTime); default: invariant(false, 'Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue.'); }}Copy the code

React 15 Mount/update React 15 Mount/update React 15 Mount/update React

What is the workinProgress. tag inside? This is the key: code


  • It finally brings us to the second core method, which is dealing with the Core logic of the React Child


The logic is pretty clear

  1. Unify the first render and update phases: Treat the first render as an update, because going from no node to node is actually an update process.
  2. The build process is changed from recursion to large loop, where each loop operates only on the nodes of the layer, returns the child to the top layer, and lets the top layer do the loop. In Async mode, such operations can be interrupted
  3. When you build it, you generate Fiber information, and that Fiber information is really just to be able to make the tree walk anywhere in a big loop when you update it later

The specific Fiber structure is as follows:


Alternate: An extremely important property. In the new Fiber architecture, we also have two Fiber trees, one old and one new (when setState is called). When our update is complete, the new Alternate tree becomes our old tree, so that the old and new can be replaced.



@Stuart Zhengmei

This is a new attribute. According to his research, Alternate is used for mine detection, mainly used for ErrorBoundary components, in an attempt to solve the problem that “ErrorBoundary” cannot save itself. Because now if ErrorBoundary makes a mistake, it will self-explode and send the mistake up to other ErrorBoundary.



Child: Because as I said before, to solve the user-mode scheduling problem, the diff patch process of each node should be controllable. Therefore, we need to change the original recursion into a cycle, which is linked by a linked list to control the diff and patch of each time. Therefore, a Fiber will have a child, Sibling, and return attributes as Pointers before and after the link tree.


EffectTag: an interesting tag used to record the type of effect. An effect refers to the way operations are performed on the DOM, such as modification, deletion, etc., and is used to Commit (similar to a database).


FirstEffect, lastEffect and other gadgets are used to save the state of effect before and after interruption, and users can resume the previous operation after interruption. This is confusing because Fiber uses interruptible architecture.


React: Fiber is a Fiber tag. Fiber is a Fiber tag


export type TypeOfWork = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10; export const IndeterminateComponent = 0; // Export const FunctionalComponent = 1; // export const ClassComponent = 2; // Class component export const HostRoot = 3; // component root component, which can be nested export const HostPortal = 4; Could be an entry point to a different renderer. export const HostComponent = 5; // export const HostText = 6; // text export const CallComponent = 7; // The component calls export const CallHandlerPhase = 8; // Call the component method export const ReturnComponent = 9; // placeholder (placeholder) export const Fragment = 10; / / fragmentCopy the code

As you can see, all the points I mentioned are completely different from the previous 15. The Fiber design of React is beyond most of the knowledge of front-end development, so I have to stick to it.


The information at hand

  1. After all, if you don’t understand React Fiber, you are advised to read it briefly. You will understand it soon after you come back
  2. All the information I found so far has been placed in Luy’s warehouse

React is not as complex as Node.js. Node.js is easy to understand as long as you have some knowledge of the operating system and network.


React’s big recursive loop to control the call stack was a surprise to me. I still remember when I used to brush leetcode (of course, I did not brush many questions), one of the problems was tree traversal, I used the stack instead of recursion, and then I looked at everyone’s answers and found that they were all two lines of code recursion. I was deeply shocked and vowed to learn recursion. Back to the tree structure, is recursive to do, because convenient, looks very high-end, so I forgot to use stack + loop to achieve.


Now a look, to understand this layer, but also brush that given… Is really no algorithm is the best, what scene with what algorithm, what problem to solve, this is the most important !!!!


Finally, I’ll show you some code, which is also a very classic example of deep traversal, to see who can see it all at once:

const mid = [] mid.push(fun1) mid.push(fun2) let i = -1 dispatch(0) function dispatch(index) { if (index <= i) return Promise.reject(new Error('next() called multiple times')) i = index let fn = mid[index] if (! fn) return Promise.resolve() try { Promise.resolve( fn('ddd', function next() { return dispatch(index + 1) }) ) } catch (e) { return Promise.reject(e) } }Copy the code