Original address: github.com/acdlite/rea…

Introduction to the

React Fiber is a refactoring of the React core algorithm that is currently in progress. It is the result of the React team’s continuous research and accumulation for more than two years.

React Fiber aims to make React better in areas like animation, layout, gestures, and more. Its central feature is incremental rendering: the rendering work can be divided into stages and performed over multiple frames.

Other important features are the ability to pause, exit, or resume current work when a new update is triggered; Different priorities can be set for different types of update work; A new concurrency model.

About this document

Fiber introduces some new concepts that are hard to understand simply by looking at the code. This document is a collection of my notes on how I understood Fiber’s implementation during the React project. As the number of notes increases, I think they may be helpful to others.

I want to use as simple a language as possible and avoid using any terminology other than clearly defined key items. If necessary, I will also provide links for you to read further.

Please note that I’m not on the React development team and don’t have any licenses. This document is not official. I asked members of the React team to review the documentation for accuracy.

This is a work in progress. Fiber is a work in progress and any meaningful refactoring will be possible before release. * Again, this document is a work in progress. Everyone is more than welcome to help me improve and make any suggestions.

My goal is that by the time you finish reading this document, you’ll understand how Fiber works, and eventually even become a React contributor.

requirements

I strongly recommend that you be very clear about the following points before proceeding:

  • React components, elements, and Instances – “components” is an important concept. A good understanding of these items is critical.
  • Reconciliation – High description of the React Reconciliation algorithm
  • React conceptual concepts – No implementation description of the React conceptual model. Some of them may not be fully understood on first reading. Don’t worry, the deeper you learn, the deeper you will understand.
  • React design principles – Need to pay more attention to this part. It goes to great lengths to explain why React Fiber is needed.

review

Please check the “Requirements” section above against your knowledge.

Before we finish anything new, let’s review some concepts.

What is a reconciliation?

reconciliation

A diff algorithm that compares 2 trees to determine which parts need to be changed.

update

Data updates drive React App UI rendering. It’s usually the result of setState. Eventually causing a re-rendering.

At the heart of the React API is thinking about whether updates will cause the entire app to be rerendered. Allow developers to live reasonably long lives without worrying about how to efficiently transition from one particular state to another in the APP (A to B, B to C, C to A, etc.).

In fact, rendering the entire app with each change only works in most small applications; In a real APP, this wasteful approach to performance would be prohibited. React has best practices applied to creating re-renders of entire apps and maintaining good performance. This best practice is called reconciliation.

The reconciliation algorithm is based on what is known as the Virtual DOM. When you render a React APP, you create a number of nodes that describe the current APP and store them in memory. For example, in a browser application, the tree will be executed in the browser and will modify some DOM properties. When an application needs an update (usually triggered by setState), a new tree is created. Compare the two trees before and after to determine what update operations need to be performed on the render tree.

Although Fiber is a complete rewrite of reconciler, it is essentially the same as the high-level algorithms described in the React documentation. The key points are:

  • Different component types are assumed to produce completely different trees. React doesn’t try to diff it. It simply replaces the old tree with a new one.
  • Diff execution requires a key. The key must be stable, predictable, and unique.

Compare Reconciliation and Render

DOM is just one of the rendering environments React can render. There are also Native IOS and Android views that can be implemented with React Native. (This is why the term “virtual DOM” is a bit of a misnomer)

React supports this target environment because React is designed as a reconciliation and rendering phase that are separate. The Reconciler is used to calculate which parts of the tree have changed; Render uses this information to re-render the app.

This separate nature means that React DOM and React Native can use their own rendering implementations, while sharing the Reconciler provided by React Core. (There is a sense of separation between mechanics and strategy)

Fiber reconstructs the Reconciler. Most of the focus is not on rendering, although rendering needs to be modified to support (and take advantage of) this new structure.

Scheduling

scheduling

The process that determines when work executes. the process of determining when work should be performed.

work

Any computation that needs to be performed. It’s usually the result of updates. (for example, setState). any computations that must be performed. Work is usually the result of an update(e.g. setState).

React’s design philosophy is clear to Scheduling, and I’m just quoting it here

In the current implementation, React recursively facilitates the entire update tree within a tick and executes the render function. In the future, however, some updates may be delayed to avoid frame loss. This is a common theme in React design. Some libraries use a ‘push’ approach, where calculations are performed as soon as a new piece of data becomes available. React, however, supports the ‘pull’ method, where calculations can be deferred until necessary. React is no ordinary library for data processes. It is a library for building user interfaces. We think its only place in an APP is to know which calculations have to be done right now and which don’t. If some operations are behind the scenes, we can delay any logic associated with them. If the data is getting faster than the frame rate, we can syndicate and do batch updates. We can increase the priority associated with user interaction (such as the animation caused by button clicks) over less important behind-the-scenes work (such as rendering new content just fetched from the web) to avoid missing frames.

Key points:

  • At the UI level, it is not necessary to perform every update immediately. In fact, doing so is very bad, causing frame drops and a poor user experience.
  • Different types of updates have different priorities – updates to complete the animation are faster than updates to modify a piece of data.
  • Pushing requires the app (you, the developer) to decide how to schedule the work. The Pull based approach allows the framework to be smart and help you make those decisions.

React does not currently use Scheduling, and updates to the entire subtree are rendered immediately. The emergence of Fiber structure drives React to reconstruct the core algorithm to take advantage of scheduling.

Now let’s start with the Fiber implementation. What follows requires more expertise than what we’ve discussed before. Before you move on, make sure you are fully aware of what you have learned.

What is the Fiber?

We’ll discuss the core of the React Fiber architecture. Fiber is much lower (and more basic?) than the typical thinking for app developers. The abstract. If you get confused in the process of learning and understanding, don’t be discouraged. Keep trying and you’ll see. (When you finally understand, please give your suggestions to make this chapter better.)

Start now!

We propose an initial target: Fiber allows React to take advantage of scheduling. In particular, we need to be able to:

  • Pause and resume work
  • Assign priorities to different types of work
  • Reuse previously completed work
  • Quit work when you don’t need to

To do this, we first need a way to separate work into separate units. In a sense, this is Fiber. A Fiber represents the work of a unit.

Let’s return to the concept that the React component takes data as a function argument, usually expressed as:

v = f (d)

So rendering a React App is similar to calling a function that calls another function inside, and so on. This analogy is useful in understanding Fiber.

A typical way for a computer to track the execution of a program is to use the Call stack. When a function is executed, a stack frame is pushed onto the stack. The stack frame represents the work performed by the function.

A stack frame is a memory management technique used in some programming languages for generating and eliminating temporary variables. In other words, it can be considered the collection of all information on the stack pertaining to a subprogram call.Stack frames are only existent during the runtime process.Stack frames help programming languages in supporting recursive functionality for subroutines. A stack frame also known as an activation frame or activation record. provenance

When dealing with UI issues, if you do too much work at once, the animation will drop frames and look stuck. What’s more, some of these jobs are unnecessary and won’t matter if they are replaced by the most recent one. This is the difference between UI components and function crashes, because components have more specific concerns than normal functions.

Newer browsers (and React Native) implement an API to help solve this problem: requestIdleCallback schedules a low-priority function to be called at idle time; RequestAnimationFrame schedules a high-priority function to be called before the next frame of animation starts. The problem is that in order to use these APIs, you have to break the rendering into incremental units. If you rely only on the call stack, it will keep working until the call stack is empty.

Wouldn’t it be great if we could customize the behavior of the call stack to optimize the render UI? Wouldn’t it be nice if we could interrupt the call stack and manipulate the stack frame manually?

This is the purpose of React Fiber. Fiber is a reimplementation of the stack, especially the React component. You can think of a single fiber as a virtual stack frame.

The advantage of reimplementing the call stack is that you can keep stack frames in memory and execute them whenever you want. This is critical to our scheduling goals.

In addition to scheduling, manual handling of stack frames will unlock more features in the future, such as concurrency mode and error boundaries. We will learn more about this in future chapters.

In the next section, we’ll learn more about the Fiber structure.

Fiber data structure

Note: The features listed below are subject to change in the future. If you find any errors or outdated information, please submit a PR.

Specifically, fiber is a JavaScript object that contains information about a component, its input/output.

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

Here are some important fields on the Fiber structure. (Complete data structure not listed)

type and key

The React element serves the same purpose as the type and key fields. (In fact, if a fiber is created based on an element, these two fields are copied directly.)

Type describes the corresponding component. For composite components, Type is either a function or the component class itself. For native components (div, SPAN, and so on), type is a string.

Conceptually, type is a function (like v = f (d)) whose execution needs to be tracked by stack frames.

Along with type, key is used to determine if a fiber can be reused in reconciliation.

child and sibling

These two fields refer to other fibers and are used to describe the recursive tree structure of one fiber.

Child Fiber corresponds to the value returned by the Render method of a component. So in the following example

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

The Parent’s child fiber corresponds to the Child.

The Sibling field is used to handle cases where return returns multiple children (new feature in Fiber) :

function Parent() {
	return [<Child1 />.<Child2 />]}Copy the code

The Child Fibers are a singly linked list with a header pointer to the first child. So in the above example, the Parent’s child is Child1, and the sibling of Child1 is Child2.

Recalling the function analogy, you can think of child Fiber as a tail-called function.

return

The value is a fiber, which should be returned when the program completes execution. Conceptually similar to returning the address of a stack frame. You can also think of it as returning its parent Fiber.

If a fiber has multiple fibers, each child’s fiber returns its parent fiber. So in our example above, the return of both Child1 and Child2 is Parent.

pendingProps and memoizedProps

Conceptually, props is an argument to a function. Fiber’s pendingProps is set at the beginning of the function execution and memoizedProps is set at the end of the function.

When the next pendingProps is equal to memoizedProps, it means that the last output of this fiber can be reused, preventing unnecessary work.

pendingWorkPriority

A number indicating the priority of the work that fiber represents. The ReactPriorityLevel module lists the different priorities and what they represent.

NoWork = 0, except for NoWork, the higher the number, the lower the priority. For example, you can use the following function to detect if a fiber’s priority is greater than a given level:

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

The example above is for demonstration purposes only, not the actual code in React Fiber

Based on this priority, the Scheduler searches for the next unit of work to be executed. This algorithm will be discussed in a future chapter.

alternate

Flush renders the output of Fiber to the screen for work-in-Progress unfinished fiber; Conceptually, it is a stack frame that has not been returned

At any given time, there are at most two fibers that correspond to a component instance: the current, flushed fiber, and work-in-Progress fiber.

const currentFiber = {
	alternate: workInProgressFiber
}

const workInProgressFiber = {
	alternate: currentFiber
}
Copy the code

The alternate for Fiber is created lazily through the cloneFiber method. Instead of always creating new objects, cloneFiber will try to reuse alternate versions of existing fiber and minimize the number of allocations.

You should think of Alternate in terms of implementation details. It shows up in code so often that it’s worth discussing it here.

output

Host Component React Application leaf. They are determined by the rendering environment (for example, in a browser, they are ‘div’, ‘span’, etc.). In JSX, they need to use lowercase letters.

Conceptually, fiber’s output is the return value of the function.

Each fiber will eventually have an output, which can only be created by the Host Component on a leaf node. The output is then sent to the tree.

Output is what is finally passed to the renderer to flush changes into the render environment. The renderer’s job is to define how outputs are created and updated.

Future chapters

That’s it, but this document is not over. Future chapters will discuss the algorithms used in the update process. It mainly contains the following contents:

  • How does scheduler find the next unit of work to execute
  • How are priorities tracked and propagated in Fiber
  • How does scheduler know when to pause and restart work
  • How does work refresh and mark completion
  • How do side effects (such as life cycle methods) work
  • What is a coroutine and how can you use it to implement features such as context and layout