React is a javascript library for building user pages. At its core, react is a mechanism for tracking component state changes and projecting the updated state onto the screen. This process is called reconciliation. By calling the setState method, the framework checks whether the state or props has changed and rerenders the component on the UI.

Virtual DOM

The React documentation provides a comprehensive overview of the mechanism: the React element, the lifecycle, the Render method, and the DIff algorithm applied to components and subcomponents. The tree that returns immutable React elements from the Render method is often referred to as the “virtual DOM.”

Fiberalgorithm

In addition to the React element tree (virtual DOM), the framework also has an internal instance tree (components, DOM nodes, etc.) to maintain state. Starting with V16.0.0, React introduced an implementation of this internal instance tree, the Fiber algorithm.

A 🌰

Here’s a simple example of a button that increments the number on the screen

Implementation method:

This is a simple component where the Render method returns two elements, button and SPAN.

The program does something after the first render and status update:

  • Update count in state
  • Retrieve and compare ClickCounter with its props
  • Update the span elements props (the contents of span are the children of span, also props)

Lifecycle Methods are called or refs are updated. All of these activities are collectively called “work” in The Fiber architecture. The work type depends on the react element type. This type is defined by the first argument to the createElement function, which is normally used to create elements in the Render method.

React has a variety of elements: class components, function components, host components (DOM nodes such as div, SPAN, etc.)

React element to fiber node

Each component in React has a UI representation, and we can call the Render method to return a view or template. Here is a template for the ClickCounter component:

The react elements

Once the template passes the JSX compiler, it is compiled into the React element, which is actually compiled into the React element before render returns, leaving ClickCounter like this:

In the Render method, the react.createElement call creates the following two data structures:

React adds the property $$Typeof to these objects to uniquely identify them as React elements.

Fiber node

During reconciliation, each React element returned by Render is merged into the Fiber node tree. Each React element has a corresponding Fiber node. Unlike the React element, the Filber node is not recreated every time the React element is rendered. The Filber node is a variable data structure used to hold component state and Dom.

As mentioned earlier, the coordination process performs different actions depending on the type of react element. In the example above, for the ClickCounter class component, it calls the lifecycle and render methods, and for host components such as SPAN, it performs a DOM mutation.

Each React element is converted to a corresponding Fiber node, which describes what needs to be done.

You can think of Fiber as a data structure that represents some work to be done, or a unit of work. The Fiber architecture also provides a convenient way to track, schedule, pause, and terminate work.

When an element of the react first be converted into a fiber node, react to use the element’s data by createFiberFromTypeAndProps function creates a fiber, in the back of the update, the react will reuse fiber node, And update the necessary attributes with data from the react element. If the react element is no longer returned from the Render function, react may also need to move the node in the hierarchy based on key, prop, or remove it.

Because React creates a fiber for each React element, we have these React element trees, which in turn will have a fiber tree. In the example program, it looks like this:

All fiber nodes are linked by a linked list. Fiber nodes have the following properties: child nodes, sibling nodes, and return nodes.

The current tree and work in the process tree

After the first rendering, React ends up with a Fiber tree that reflects the state of the rendered UI application, usually called Current. When React starts processing updates, it builds a so-called workInProgess tree that reflects the state that will be flushed to the screen later.

WorkInProgess tree

All the work is done in Fiber in the workInProgress tree. When React traverses the current tree, it creates an **alternate ** for each existing Fiber node that makes up the workInProgress tree, which is created using data from the React element returned by the Render method. Once the update is processed and all related work is done, React will flush the alternate tree to the screen, and once the workInProgress tree is rendered on the screen, it will become the Current tree.

The core principle of React is consistency. React always updates the DOM at once and does not display only partial results. The workInProgress tree acts as a “draft” that is invisible to the user. So React processes all the components first and then flushes their updates to the screen.

In the source code, you can see that there are many functions that get fiber nodes from the Current and workInProgress trees, such as:

Each Fiber node holds a corresponding reference from another tree in a standby field, for example, a node in the current tree points to a node in the workInProgress tree, and vice versa.

Effects:

We can think of the react component as a function that uses state and props to evaluate the UI presentation. Every activity, such as a Dom mutation or invocation of a lifecycle method, should be considered a side effect. The document also mentions:

You’ve likely performed data fetching, subscriptions, Or manually changing the DOM from React components before. We call these operations “side effects” (or “effects” for) Short) because they can affect other components and can’t be done during rendering.

You’ve probably done data fetching, subscribing, or manually changing the DOM from the React component before. We call these actions “side effects” (or “effects” for short) because they affect other components and cannot be done during rendering.

Most updates to state and props cause effects. In addition to the update function, fiber nodes also have a convenient mechanism for tracking effects, and each fiber node has its associated effects. They are encoded in the effectTag field.

So The Effects in Fiber basically defines what the instance needs to do after processing the update. For the Host component (div, SPAN, P, etc.), the work involves adding, updating, and deleting elements. For class components react may need to update refs and call the componentDidMount and componentDidUpdate lifecycle methods, as well as other types of Fiber corresponding effects.

Effects list

The React update process is very fast, and it takes a few techniques to achieve this level of performance. One is to build a linear list of Fiber nodes for quick iteration. A linear list of iterations is much faster than a tree, and there is no need to spend time on nodes with no side effects.

The goal of this list is to tag nodes that have Dom updates or other effects associated with them. This list is a subset of the finishedWork tree and is linked using nextEffect properties rather than the subproperties used in the Current and workInProgress trees.

To visualize this, let’s assume that the nodes highlighted in the fiber node tree below have some work to do. For example, the update will cause C2 to be inserted into THE Dom, D2, and C1 to change properties, and cause B2 to start the lifecycle methods. Effects list links them together so React can skip other nodes:

You can see how the nodes with the effects are linked together. When traversing a node, React uses the firstEffect pointer to indicate where the list starts. So the list above can be represented as a linear list, as follows:

The root element of the Fiber tree

Every React application has one or more Dom elements that act as containers. In our example, this is the DIV element that contains the Id container.

React creates a Fiber object for each container, which you can access using a Dom reference:

This fiberRoot is the reference point for the React save Fiber tree, which is stored in the fiberRoot current property:

Fiber trees start with a special type of Fiber node, HostRoot. It is created internally as the parent of the topmost component, another link back to FiberRoot from the HostRoot Fiber node via the setNode property.

You can explore the Fiber tree by accessing the topmost HostRoot Fiber node via the Fiber root, or get a separate Fiber node from a component instance:

Fiber node structure

Here is the structure of the Fiber node created by the ClickCounter component:

And the SPAN Dom element:

There are quite a few fields in the Fiber node. We’ve already talked about alternate, effectTag, and nextEffect fields. Now let’s look at the other fields.

stateNode

Saves references to component class instances, Dom nodes, or other React element types associated with fiber nodes. In general, we can say that this property is used to hold the local state associated with fiber.

type

Defines the functions or classes associated with fiber. For class components, it points to constructors, and for Dom elements, it specifies HTML tags.

tag

Define the type of fiber. It is used in coordination algorithms to determine what needs to be done. For example, as mentioned earlier, work varies depending on the type of the react element. CreateFiberFromTypeAndProps () function converts a react element is mapped to the corresponding node type fiber. In our application, the ClickeCounter component has a tag of 1, representing a ClassComponent, and the SPAN element has a tag of 5, representing a HostComponent.

updateQueue

Queues for state updates, callbacks, and Dom updates.

memoizedState

State of the fiber used to create the output. As updates are processed, it reflects the current state rendered on the screen.

memoizedProps

Props used to create output fiber during the previous rendering

pendingProps

The props updated with new data in the React element need to be applied to child components or Dom elements.

key

A unique identifier with a set of subitems that helps determine which items have changed and which items have been added or removed from the list. It is related to the list and key feature described by React

You can find the complete structure of a Fiber node here. Some fields are omitted from the above parsing, such as the pointer’s child, sibling, and return elements. And a class of Scheduler-specific fields such as expirationTime, childExpirationTime, and Mode.

General algorithm

React coordination has two main phases: render and commit.

During the first Render phase, React applies updates to components scheduled via setState or react. Render and finds out what needs to be updated in the UI. For a first render, React creates a fiber node for each react element returned by the Render method. In future updates, fiber for existing React elements will be reused and updated. The result of this phase is a fiber node tree labeled side-effect. This effect describes the work that needs to be done in the next commit phase. During the COMMIT phase, React takes the Fiber tree marked with an effect and applies it to the instance. You then iterate through the Effects List (described above) and perform Dom updates and other changes visible to the user.

The first Render phase can be executed asynchronously. React can process one or more Fiber nodes, depending on the time available, then stop the finished work and fire some events. And then pick up where you left off. But sometimes you throw away what you’ve already done and start from scratch. You can pause because the work performed at this stage does not result in changes visible to the user, such as Dom updates. Instead, the ** COMMIT phase below is always synchronous **. This is because the work performed in this phase results in changes visible to the user, such as Dom updates. This is why React needs to update the Dom once.

Calling life-cycle methods is one of the types of work react performs. Some methods are called in the Render phase, others in the Commit phase.

The render phase calls the following methods:

  • [UNSAFE_]componentWillMount (deprecated)
  • [UNSAFE_] componentWillReceiveProps (deprecated) / / component will receive props (deprecated)
  • GetDerivedStateFromProps // Gets the derived state from props
  • ShouldComponentUpdate // Whether the component should be updated
  • [UNSAFE_]componentWillUpdate (deprecated)

Some legacy lifecycle methods performed during the render phase are marked as unsafe for version 16.3 ([UNSAFE_]) and will be deprecated in future versions of 16.x, while their counterparts without unsafe prefixes will be removed in 17.0. See more

Methods called in the COMMIT phase:

  • GetSnapshotBeforeUpdate // Obtain the snapshot before the update
  • ComponentDidMount // The component is already mounted
  • ComponentDidUpdate // Components have been updated
  • ComponentWillUnmount // The component will be unmounted

Because these methods are executed during the synchronous commit phase, they can contain side effects and affect the Dom.

Render stage

The coordination algorithm always starts at the top HostRoot Fiber stage using the renderRoot function. React skips fiber nodes that have already been processed. Until you find the fiber node that completes the work. For example, if setState is called deep in the component tree, React will start at the top but quickly skip the parent node until it reaches the component whose setState method is called.

Main steps of workLoop (workLoop function)

All fiber nodes are handled in the workLoop function. Here is part of the code implementation:

nextUnitOfWork

In the code above, nextUnitOfWork saves a reference to a Fiber node in the workInProgress tree that has some work to do. When React traverses the Fiber tree, it uses this variable to know if any other fiber nodes have not completed their work. After processing the current fiber node, this variable will contain a reference to the next fiber node, or null. When null, React exits the workLoop and is ready to commit the changes.

Four main functions

There are four functions primarily used to traverse the tree and start or finish work:

  • PerformUnitOfWork // Executes the unit of work
  • BeginWork // beginWork
  • CompleteUnitOfWork // Complete the unit of work
  • CompleteWork // Complete the work

Here is an animation of traversing the Fiber tree. On the right is a simplified implementation of the functions, each of which needs to deal with a fiber node. As react moves down the tree, you can see changes to the currently active Fiber node. This animation clearly shows how the algorithm moves from one branch to another, completing the work of children first and then moving to parent.

Note: Vertical branches represent siblings and horizontal branches represent children.

PerformUnitOfWork and beginWork

The function performUnitOfWork accepts a fiber node from the workInProgress tree and starts the work by calling the beginWork function. This function launches all the activities that Fiber needs to perform. The beginWork function always returns a pointer to the next child of the loop to be processed or null.

If there is a next element, it will be assigned to the variable nextUnitOfWork in the workLoop function. However, if there are no child nodes, React knows that it has reached the end of the branch and can therefore complete the current node. Once the node is complete, it will need to perform work for its sibling nodes and then backtrack to the parent node. This is done in the completeUnitOfWork function:

As you can see, the gist of this function is a big while loop. React enters this function when the workInProgress node has no children. When it finishes working on the current fiber, it checks to see if there are any fibers of the same class. If found, React exits the function and returns a pointer to the sibling. It will be assigned to the nextUnitOfWork variable, from which React will perform branch work.

It is important to understand that it does not finish the work of the parent node and traceback until all branches starting with the child node have been completed.

The completeUnitOfWork function is primarily used for iteration purposes, with the main activity occurring in the beginWork and completeWork functions.

Commit Phase

This stage starts with the function completeRoot. This is where React updates the DOM and calls the pre-mutation and post-mutation lifecycle methods.

When React reaches this stage, it has two trees and an effect list (described earlier). The first tree represents the state currently displayed on the screen. The other is an alternate tree built during the rendering phase.

Called finishWork or workInProgress in the source code, it represents the state that needs to be reflected on the screen.

The alternate tree is similarly linked to the current tree through child and sibling Pointers.

There is then an Effect List, which links a subset of nodes in the finishedWork tree through the NextTeffect pointer. Keep in mind that the side effects list is the result of running the Render phase. The key to rendering is determining which nodes need to be inserted, updated, or removed, and which component lifecycle methods need to be invoked. It is a set of nodes that iterates through the commit phase.

commitRoot

The main function that the commit phase runs is commitRoot, which basically does the following:

  1. There are snapshots in the mark (Snapshot)getSnapshotBeforeUpdateLifecycle approach
  2. In marked with delete (Deletion)ComponentWillUnmountLifecycle approach
  3. Perform all DOM inserts, updates, and deletions
  4. willfinishedWorkTree set to current
  5. There are places in the mark (Placement)ComponentDidMountLifecycle approach
  6. There are updates in the tag (Update)ComponentDidUpdateLifecycle approach

After calling the getSnapshotBeforeUpdate method, React commits all side effects in the tree. It is divided into two stages:

The first stage performs all Dom inserts, updates, deletes, and ref offloads. React then assigns finishedWork to FiberRoot and marks the workInProgress tree as the current tree, which is done during the first traverse of the commit phase, so the previous tree is still current during componentWillUnmount, and before the second phase, ComponentDid Mount/Update during the tree is current.

The second react phase calls all the other lifecycle methods, the ref callback. These methods are executed as separate passes, so all drops, updates, and deletions in the entire tree are called.

Here is the gist of the function that runs the above steps:

Pre-mutation life cycle approach

For example, the following code traverses the effect tree and checks if a node has a Snapshot effect:

For class components, this effect means calling the getSnapshotBeforeUpdate lifecycle method.

Update the DOM

CommitAllHostEffects is a function that performs Dom updates on react. This function basically defines the types of operations that a node needs to perform:

React calls componentWillUnmount as part of the commitDeletion process.

Post-mutation life cycle approach

CommitAllLifecycles is a function where React calls all remaining lifecycle methods componentDidUpdate and componentDidMount.

End scatter flower (^o^)

The article based on