introduce

Some react terms are not translated. The original

React Fiber is a re-implementation of the React core algorithm. This is an ongoing project. The React team has conducted two years of research and investigation so far.

When Reacting Fiber is used, React Fiber can be used in animation, layout, and gestures. The first feature of React Fiber is incremental rendering. What is incremental rendering? It is a way to slice the rendering work into chunks and divide it into multiple frames.

Key features include pauses, interrupts, and reusing work in the update process; Assign different priorities to different types of update tasks; Set the stage for concurrency.

About this Document

React Fiber introduces several novel concepts. These concepts often cannot be understood simply by looking at the source code. This document is purely a personal note I took while following up on the details of the React Fiber project. As I remembered more and more of these notes, I realized that they might be useful to others, too.

I’m going to try to describe and express this in simple, easy-to-understand language, and avoid giving you a hard definition of terms at the outset. I also try to link to some of the more important external sources.

Note that I’m not part of the React team and I don’t speak with authority. Therefore, this is not an official authoritative document. However, I have consulted with several React team members to ensure the accuracy of what is being said.

This document is subject to change as I have a feeling that the React Fiber project will be undergoing some major refactoring at any time before it is completed. Of course, I also try to document its design changes. Any improvements and suggestions for this document are welcome.

My goal is that after reading this document, you’ll know enough about React Fiber as you follow through on the parts it’s already implemented. Eventually, you can even make your own contribution to the React community.

Pre-knowledge reserve

Before continuing, I strongly encourage you to take a look at the following articles:

  • React Components, Elements, and Instances-“Component” is a term used over and over again. Familiarity with these terms is crucial.
  • Reconciliation- a quality explanation of the React Reconciliation algorithm
  • React Basic Theoretical Concepts- a Theoretical model for React Basic Concepts.
  • React Design Principles- Read the “Scheduling” section. This section does a great job of explaining why React Fiber was adopted.

To relive

Please check to see if you have read the “pre-knowledge” section. If not, I suggest you read it. Before we dive into anything new, let’s go over a few concepts.

What is Reconciliation?

reconciliation

React is an algorithm that diff two nodes (such as the React Element) of a tree to determine which parts need to be updated.

update

The React App rerenders the data. Normally, setState will cause an update. Rerender exists as a result of the update.

The central idea of the React API is that an update is equivalent to a re-render of the entire app. This design helps developers to think declaratively about how the app effectively transitions state (A to B, B to C, C to A), rather than having to worry about the details of the time in an imperative way.

However, every data change that causes the entire app to be re-rendered only happens on some of the less important apps. In the real world, you don’t do that. Because doing so leads to a very expensive performance drain. React has helped us with performance optimizations along with the latest interface. Most of these optimizations are part of our reconciliation algorithm.

The reconciliation algorithm is behind what is commonly known as the “virtual DOM”. A high-level description would look like this: When you render a React app, react generates a tree of nodes that describe the app and stores it in memory. The node tree is then flushed to the appropriate rendering platform – for example, for browser applications, “node tree flushed to the appropriate rendering platform” is a series of DOM operations. Once the app has been updated (by calling setState), a new node tree will be generated. The new node tree is compared to the old node tree in memory to calculate the specific actions needed to update the entire app interface.

Although the React Fiber is a rewrite of reconciler, according to the description of the high-level algorithm in the React Doc, there are still a large number of similarities before and after the rewrite. Two key points are:

  • Assume that components of different “component types” generate roughly different tree of nodes. In cases like this (updates to components of different “component types”), React does not use the diff algorithm on them, but simply replaces the old node tree with the new one completely.
  • Use the prop key to diff lists. The key should be “stable, predictable, and unique”.

reconciliation vs rendering

DOM is just one of the rendering platforms React can adapt to. Other major rendering platforms include the View layer for IOS and Android (with the React Native renderer).

React works well on so many rendering platforms because react is designed as two separate stages: Reconciliation and Rendering. Reconciler calculates which parts of the tree of nodes need to be changed by comparing them; The Renderer is responsible for using these calculations to do some actual interface updates.

This separate design means that React DOM and React Native can use separate renderers while sharing the same reconciler, provided by React Core. React Fiber rewrote the Reconciler, but that has little to do with Rendering. However, many renderers will definitely need to make some adjustments to integrate the new architecture.

Scheduling

Scheduling

Decide when that part of work should be executed

work

All calculations that need to be performed. Work generally exists as a result of an update.

The React Design Principles document is very good on this topic, so I’ll use it here:

In the current implementation, React recursively traverses the tree of nodes, calling the render method on the updated tree accordingly. However, in the future React will delay certain updates to avoid dropping frames for interface updates.

This is a common theme in React design. Some popular libraries implement the “push” approach where computations are performed when the new data is available. React, however, sticks to the “pull” approach where computations can be delayed until necessary.

React is not a general-purpose data processing library. It is a library for building user interfaces. The ability to know which app calculations are relevant to the current update and which are not has unique advantages in building user interfaces.

If something is invisible to the user through the screen, we can delay the execution of its associated logic. If the data is arriving faster than the frame rate, we can merge the data and use a batch update strategy. We can prioritize work from the user interface (say, an animation triggered by a button click) over less important work running in the background (rendering something onto the screen via a web request). By doing this, we can prevent the user from seeing the interface drop frames.

The key points of the document given above are as follows:

  • In UI development, it is not necessary to implement every update request. In fact, if you do this, the interface will drop frames, which greatly degrades the user experience.
  • Different types of update requests should have different priorities. For example, executing an animation should have a higher priority than an update from the Data Store.
  • Push-based solutions require you (the developer) to manually schedule work. Pull-based solutions make react smart and do the scheduling for you.

React Current (2016) application of Scheduling is not large enough. An update means (roughly) a complete re-rendering of the entire node tree. The core algorithm of React is rewritten to take advantage of scheduling. That’s probably what the React Fiber project was all about.

Now we’re ready to dive into the React Fiber implementation. In the next section, we’re going to talk more and more about technical things. Before moving on, make sure you have a good understanding of what the previous chapter says.

What is React Fiber?

We’re going to talk about the core of React Fiber. React Fiber is a lower level of abstraction than you might think. If you find yourself struggling to understand the architecture, don’t be discouraged. Don’t give up, keep trying, and you’ll figure it out eventually (when you finally understand React Fiber, please give me some advice on how to improve this section).

We have confirmed above that the primary goal of React Fiber is to enable React to integrate scheduling. More specifically, we can do the following:

  • Interrupt work and resume work later
  • Assign equal priority to different types of work
  • Reuse previously completed work
  • If a work is no longer needed, interrupt it.

In order to achieve the above points, we need to break work into units. This unit is actually the React Fiber. A React Fiber represents a work unit.

React components as functions of data React components as functions of data

v = f(d) // view = f(data)
Copy the code

Therefore, we can make the equivalent mental model: rendering a React app is equivalent to calling a function whose body contains calls to other functions…… With this kind of push. This mental model is extremely useful for understanding React Fiber.

The computer tracks the progress of the program through the Call Stack. Once a function is executed, it becomes a stack frame and is added to the stack. This stack frame represents the work that this function is doing.

The problem is that when working with the UI, there’s a lot of work being done at once. This causes the animation to drop frames and not look smooth. Also, it is not necessary to perform some work immediately if it will be replaced by later work. This is where the comparison between UI components and function breaks down, because components have more specific concerns than functions in general.

Newer rendering platforms such as React Native implement apis specifically designed to deal with this problem: requestIdleCallback and requestAnimationFrame. RequestIdleCallback is responsible for scheduling low-priority functions to be executed during idle periods; RequestAnimationFrame is responsible for scheduling a high-priority function to be called during the next animation frame. The problem is that in order to be able to use the API, you have to find a way to break down the rendering work into incremental units. If we just rely on the Call Stack, it doesn’t seem to work. Because the Call Stack executes the work unit until the Call Stack empties.

Wouldn’t it be great to be able to optimize UI rendering by customizing the behavior of the Call Stack? Wouldn’t it be great if we could manually interrupt the Call Stack and manipulate every frame of the Call Stack?

And that’s what React Fiber is all about. React Fiber is a reimplementation of stack, specifically for the React component. You can think of a single Fiber as a virtual stack frame.

The nice thing about reimplementing stack is that you can keep stack frames in memory and execute them whenever and wherever you want. This is important to achieve the goals we set when scheduling was introduced.

Rewriting the stack makes it possible to handle stack frames manually, in addition to scheduling, to implement new features such as concurrency and error boundaries. We will discuss these topics in future chapters.

In the next section, we’ll take a closer look at a Fiber data structure.

Fiber data structure

Note: As you get deeper into the technical details, the likelihood of changes to those details increases. If you find any errors or outdated information, please send me a PR.

Specifically, a fiber is just a javascript object. This object contains some information about the component: the input of this component, and the output of this component.

A fiber corresponds to a stack frame and also corresponds to an instance of a component.

Here are some of the more important fields of the Fiber object (this list is also not exhaustive).

The type and the key

The Type and key fields of the Fiber object have the same meanings as the Type and key fields of the React Element. In fact, the Fiber object is created from the React Element, and these two fields are copied directly from the react Element at creation time.

The Type field of the Fiber object describes the component to which it corresponds. For composite Components, the type field value is either a function or a class component. For host Components (for example, div, SPAN, etc.), the value of the Type field is a string.

In theory, the value of the type field is the function whose execution is tracked by the Stack Frame (v = f(d)).

Like the Type field, the key field is used during reconciliation to decide whether to reuse the fiber.

The child and (

These two fields refer to other fibers and together form a fiber tree with a recursive structure.

Child Fiber is the return value of the render method of the component. Here’s an example:

function Parent(){
    return <Child />
}
Copy the code

Parent’s child fiber is child.

Sibling field exists on multiple children returned from Render (new feature in Fiber) :

function Parent() {
  return [<Child1 />, <Child2 />]
}

Copy the code

All of the above child fibers form a single linked list. The first child is the head node of the list. In this example, Child1 is the Parent’s child fiber, and Child2 is the Sibling child of Child1.

Going back to our previous analogy with function, you can think of Child Fiber as a tail-called function.

return

Return Fiber refers to the fiber that the program returns after processing the current fiber. Conceptually, It is conceptually the same as the return address of a stack frame. We can also think of it as the parent fiber.

If a fiber has more than one child fiber, the child fiber’s return fiber is its parent fiber. So, in the example we mentioned above, the return fiber for Child1 and Child2 is Parent.

PendingProps and memoizedProps

Conceptually, props is an argument to a function. When the function starts executing, Fiber’s pendingProps will be set, and when the function is finished, Fiber’s memoizedProps will be set.

When the function is executed again, a new pendingProps is computed. If it is the same as Fiber’s memoizedProps field, this tells us that Fiber’s previous output can be reused, preventing unnecessary work.

pendingWorkPriority

A value used to indicate priority. Whose priority? The priority of work represented by fiber. The ReactPriorityLevel module lists the priorities of all the works and also lists what they represent.

With the exception of “noWork” (which has a priority value of 0), a higher value indicates a lower priority. For example, you can use the following function to check whether the current fiber priority is higher than the given priority:

function matchesPriority(fiber, priority) { return fiber.pendingWorkPriority ! == 0 && fiber.pendingWorkPriority <= priority }Copy the code

The above function is for demonstration purposes only, and it doesn’t come from the actual React Fiber source code.

The Scheduler consumes Fiber’s Priority field to determine which unit of work needs to be executed next. The algorithms involved will be discussed in the Future section.

alternate

flush

When we flush a fiber, we flush the output of the fiber onto the screen.

work-in-progress

When a fiber has not yet completed, work is said to be work-in-progress. The concept is when a stack frame has not been returned.

At any time, there are at most two flushed fibers that are uploaded to a component instance: fiber that is currently flushed and fiber that is uploaded as work-in-Progress.

Current fiber and work-in-Progress fiber will be converted to each other. The current fiber will eventually be converted into the fiber of work-in-Progress, and the fiber of work-in-Progress will eventually be converted into the current fiber when the next work starts.

An alternate fiber of a fiber is lazily created by a function called cloneFiber. Instead of always creating a new object, cloneFiber will try to reuse its alternate fiber if it exists. By doing so, you can reduce memory allocation.

The alternate field is already an implementation detail in React Fiber. Because it appears so frequently in the source code, it’s worth discussing here.

output

host component

The React App leaf node. They represent specific rendering platforms (for example, DOM nodes such as “div”, “span” and so on are host Components for browser apps). In JSX, they all appear as lowercase tag names.

Conceptually, the return value of a function is the output of a fiber.

Each fiber will eventually have its own output, which can only be created in the leaf by the Host component. Output is created and passed up the node tree.

Output is eventually passed to the Renderer, and the application renders the latest state on the screen. It’s the renderer’s job to define how outputs are created and updated to the screen.

Future sections

That’s all I’ve got so far. However, this is an unfinished document. In future chapters, I will describe an algorithm used throughout the update process. Topics covered are as follows:

  • How does scheduler find out who the next Work unit is?
  • How are priorities tracked and propagated in fiber Tree?
  • How does scheduler know when to pause and resume work?
  • How is work flushed and marked “done”?
  • How does side-effect (such as life cycle methods) work?
  • What is a coroutine? How is it used to implement features such as context and layout?

Related Videos

  • What’s Next for React (ReactNext 2016)