React Fibre Architecture
React Fibre Architecture
## Introduction
The goal of React Fiber is to increase its suitability for areas like animation,
layout, and gestures. Its headline feature is **incremental rendering**: the
ability to split rendering work into chunks and spread it out over multiple frames.
Other key features include the ability to pause, abort, or reuse work as new
updates come in; the ability to assign priority to different types of updates; and
new concurrency primitives.
Fiber introduces several novel concepts that are difficult to grok solely by
looking at code. This document began as a collection of notes I took as I followed
along with Fiber's implementation in the React project. As it grew, I realized it
may be a helpful resource for others, too.
I'll attempt to use the plainest language possible, and to avoid jargon by
explicitly defining key terms. I'll also link heavily to external resources when
possible.
Please note that I am not on the React team, and do not speak from any authority.
**This is not an official document**. I have asked members of the React team to
review it for accuracy.
This is also a work in progress. **Fiber is an ongoing project that will likely
undergo significant refactors before it's completed.** Also ongoing are my attempts
at documenting its design here. Improvements and suggestions are highly welcome.
My goal is that after reading this document, you will understand Fiber well enough
to [follow along as it's
implemented](https://github.com/facebook/react/commits/master/src/renderers/
shared/fiber), and eventually even be able to contribute back to React.
### Prerequisites
I strongly suggest that you are familiar with the following resources before
continuing:
## Review
Please check out the prerequisites section if you haven't already.
Before we dive into the new stuff, let's review a few concepts.
<dl>
<dt>reconciliation</dt>
<dd>The algorithm React uses to diff one tree with another to determine which
parts need to be changed.</dd>
<dt>update</dt>
<dd>A change in the data used to render a React app. Usually the result of
`setState`. Eventually results in a re-render.</dd>
</dl>
The central idea of React's API is to think of updates as if they cause the entire
app to re-render. This allows the developer to reason declaratively, rather than
worry about how to efficiently transition the app from any particular state to
another (A to B, B to C, C to A, and so on).
Actually re-rendering the entire app on each change only works for the most trivial
apps; in a real-world app, it's prohibitively costly in terms of performance. React
has optimizations which create the appearance of whole app re-rendering while
maintaining great performance. The bulk of these optimizations are part of a
process called **reconciliation**.
The DOM is just one of the rendering environments React can render to, the other
major targets being native iOS and Android views via React Native. (This is why
"virtual DOM" is a bit of a misnomer.)
The reason it can support so many targets is because React is designed so that
reconciliation and rendering are separate phases. The reconciler does the work of
computing which parts of a tree have changed; the renderer then uses that
information to actually update the rendered app.
This separation means that React DOM and React Native can use their own renderers
while sharing the same reconciler, provided by React core.
### Scheduling
<dl>
<dt>scheduling</dt>
<dd>the process of determining when work should be performed.</dd>
<dt>work</dt>
<dd>any computations that must be performed. Work is usually the result of an
update (e.g. <code>setState</code>).
</dl>
> In its current implementation React walks the tree recursively and calls render
functions of the whole updated tree during a single tick. However in the future it
might start delaying some updates to avoid dropping frames.
>
> 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 generic data processing library. It is a library for building user
interfaces. We think that it is uniquely positioned in an app to know which
computations are relevant right now and which are not.
>
> If something is offscreen, we can delay any logic related to it. If data is
arriving faster than the frame rate, we can coalesce and batch updates. We can
prioritize work coming from user interactions (such as an animation caused by a
button click) over less important background work (such as rendering new content
just loaded from the network) to avoid dropping frames.
- In a UI, it's not necessary for every update to be applied immediately; in fact,
doing so can be wasteful, causing frames to drop and degrading the user experience.
- Different types of updates have different priorities — an animation update needs
to complete more quickly than, say, an update from a data store.
- A push-based approach requires the app (you, the programmer) to decide how to
schedule work. A pull-based approach allows the framework (React) to be smart and
make those decisions for you.
---
Now we're ready to dive into Fiber's implementation. The next section is more
technical than what we've discussed so far. Please make sure you're comfortable
with the previous material before moving on.
## What is a fiber?
We're about to discuss the heart of React Fiber's architecture. Fibers are a much
lower-level abstraction than application developers typically think about. If you
find yourself frustrated in your attempts to understand it, don't feel discouraged.
Keep trying and it will eventually make sense. (When you do finally get it, please
suggest how to improve this section.)
Here we go!
---
We've established that a primary goal of Fiber is to enable React to take advantage
of scheduling. Specifically, we need to be able to
In order to do any of this, we first need a way to break work down into units. In
one sense, that's what a fiber is. A fiber represents a **unit of work**.
```
v = f(d)
```
It follows that rendering a React app is akin to calling a function whose body
contains calls to other functions, and so on. This analogy is useful when thinking
about fibers.
The way computers typically track a program's execution is using the [call stack]
(https://en.wikipedia.org/wiki/Call_stack). When a function is executed, a new
**stack frame** is added to the stack. That stack frame represents the work that is
performed by that function.
When dealing with UIs, the problem is that if too much work is executed all at
once, it can cause animations to drop frames and look choppy. What's more, some of
that work may be unnecessary if it's superseded by a more recent update. This is
where the comparison between UI components and function breaks down, because
components have more specific concerns than functions in general.
Newer browsers (and React Native) implement APIs that help address this exact
problem: `requestIdleCallback` schedules a low priority function to be called
during an idle period, and `requestAnimationFrame` schedules a high priority
function to be called on the next animation frame. The problem is that, in order to
use those APIs, you need a way to break rendering work into incremental units. If
you rely only on the call stack, it will keep doing work until the stack is empty.
The advantage of reimplementing the stack is that you can [keep stack frames in
memory](https://www.facebook.com/groups/2003630259862046/permalink/
2054053404819731/) and execute them however (and *whenever*) you want. This is
crucial for accomplishing the goals we have for scheduling.
Aside from scheduling, manually dealing with stack frames unlocks the potential for
features such as concurrency and error boundaries. We will cover these topics in
future sections.
*Note: as we get more specific about implementation details, the likelihood that
something may change increases. Please file a PR if you notice any mistakes or
outdated information.*
Here are some of the important fields that belong to a fiber. (This list is not
exhaustive.)
The type and key of a fiber serve the same purpose as they do for React elements.
(In fact, when a fiber is created from an element, these two fields are copied over
directly.)
The type of a fiber describes the component that it corresponds to. For composite
components, the type is the function or class component itself. For host components
(`div`, `span`, etc.), the type is a string.
Conceptually, the type is the function (as in `v = f(d)`) whose execution is being
tracked by the stack frame.
Along with the type, the key is used during reconciliation to determine whether the
fiber can be reused.
These fields point to other fibers, describing the recursive tree structure of a
fiber.
The child fiber corresponds to the value returned by a component's `render` method.
So in the following example
```js
function Parent() {
return <Child />
}
```
The child fiber of `Parent` corresponds to `Child`.
The sibling field accounts for the case where `render` returns multiple children (a
new feature in Fiber!):
```js
function Parent() {
return [<Child1 />, <Child2 />]
}
```
The child fibers form a singly-linked list whose head is the first child. So in
this example, the child of `Parent` is `Child1` and the sibling of `Child1` is
`Child2`.
Going back to our function analogy, you can think of a child fiber as a [tail-
called function](https://en.wikipedia.org/wiki/Tail_call).
#### `return`
The return fiber is the fiber to which the program should return after processing
the current one. It is conceptually the same as the return address of a stack
frame. It can also be thought of as the parent fiber.
If a fiber has multiple child fibers, each child fiber's return fiber is the
parent. So in our example in the previous section, the return fiber of `Child1` and
`Child2` is `Parent`.
When the incoming `pendingProps` are equal to `memoizedProps`, it signals that the
fiber's previous output can be reused, preventing unnecessary work.
#### `pendingWorkPriority`
A number indicating the priority of the work represented by the fiber. The
[ReactPriorityLevel](https://github.com/facebook/react/blob/master/src/renderers/
shared/fiber/ReactPriorityLevel.js) module lists the different priority levels and
what they represent.
```js
function matchesPriority(fiber, priority) {
return fiber.pendingWorkPriority !== 0 &&
fiber.pendingWorkPriority <= priority
}
```
*This function is for illustration only; it's not actually part of the React Fiber
codebase.*
The scheduler uses the priority field to search for the next unit of work to
perform. This algorithm will be discussed in a future section.
#### `alternate`
<dl>
<dt>flush</dt>
<dd>To flush a fiber is to render its output onto the screen.</dd>
<dt>work-in-progress</dt>
<dd>A fiber that has not yet completed; conceptually, a stack frame which has not
yet returned.</dd>
</dl>
At any time, a component instance has at most two fibers that correspond to it: the
current, flushed fiber, and the work-in-progress fiber.
The alternate of the current fiber is the work-in-progress, and the alternate of
the work-in-progress is the current fiber.
You should think of the `alternate` field as an implementation detail, but it pops
up often enough in the codebase that it's valuable to discuss it here.
#### `output`
<dl>
<dt>host component</dt>
<dd>The leaf nodes of a React application. They are specific to the rendering
environment (e.g., in a browser app, they are `div`, `span`, etc.). In JSX, they
are denoted using lowercase tag names.</dd>
</dl>
Every fiber eventually has output, but output is created only at the leaf nodes by
**host components**. The output is then transferred up the tree.
The output is what is eventually given to the renderer so that it can flush the
changes to the rendering environment. It's the renderer's responsibility to define
how the output is created and updated.
## Future sections
That's all there is for now, but this document is nowhere near complete. Future
sections will describe the algorithms used throughout the lifecycle of an update.
Topics to cover include: