Skip to main content

How Fibers creation works ⏸️

note

This section is a ⏸️ break, we won't continue from where we stopped in the previous section, but we will take the discussion to another subject as important before going back.

In this section, we will discover how, where and when all fiber's creation occur inside React.

We've seen so many, so this is like an extension, but we will restart from the beginning.

How creating the first fibers works

The first created fiber refers to the fiberRoot.current property.

Let's restart from the begging and go by a simple example:

import * as React from "react";
import { createRoot } from "react-dom/client";

// 1
const root = createRoot(document.getElementById("root"));

// 2
root.render(<App />);

function App() {
const rerender = useRerender();
return (
<div>
<h2>Hello, world!</h2>
<h3>Hello again</h3>
<button onClick={rerender}>rerender</button>
</div>
);
}

// ignore this, used to rerender our component
function useRerender() {
return React.useState(0)[1].bind(null, (prev) => prev + 1);
}

During step 1, right after calling createRoot(container), here is the tree at that time:

Result right after createRoot()

Next, during step 2 which will schedule things are perform lots of work, React will construct the alternate tree, because we didn't show (commit) anything yet:

After root.render() and before commit

This creation was performed inside reconcileChildren seen in how reconciliation works.

After commit, the root.current.alternate will take over and be the new current:

After rendering and committing

This process of swapping the alternate tree with the current tree is what will happen all the time at runtime.

On updates, here is what our tree will look like:

Tree on updates

To visualize this for yourself, go to any of your React applications (or if you want, here is the link to the sandbox with our used example) execute the following in the console of your browser:

// assuming there is one root
[...window.__REACT_DEVTOOLS_GLOBAL_HOOK__.getFiberRoots(1)][0];

This will give you the first root, access root.current and start looking at child and sibling and alternate properties.

How the alternate works

What is the alternate

The alternate is a FiberNode object. It is created when a fiber is about to render. It is created from the current painted tree.

React can render several times like we saw earlier before committing.

Alternate fibers are created during reconciliation from the parent:

Let's go back to our previous example where we trigger an update from our App component.

function App() {
const rerender = useRerender();
return (
<div>
<h2>Hello, world!</h2>
<h3>Hello again</h3>
<button onClick={rerender}>rerender</button>
</div>
);
}

Let's suppose the triggerred update is with a SyncLane which will result in rendering it immediately.

This will first create the alternate fiber for the App component's fiber, then render it and obtain new children.

Then, it will call reconcileChildren like this:

reconcileChildren(
// the returnFiber (the new tree)
App_Alternate_Fiber,
// the first child of the current tree
div_Current_Fiber,
// the new children: a ReactNode: our div
{
type: "div",
$$typeof: Symbol("react.element"),
props: {
children: [
// h2
{$$typeof: Symbol("react.element"), type: "h2", props: {children: "Hello, world!"}},
// h3
{},
]
}
},
// the renderLanes
SyncLane
)

reconcileChildren will actually attach the first child to the current alternate.

function reconcileChildren(
current, workInProgress, nextChildren, renderLanes
) {
workInProgerss.child = ...
}

How the alternate is created

The fiber and alternate are both instances of FiberNode , the only instantiation using the FiberNode constructor occurs in the createFiber function:

function createFiber(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
): Fiber {
return new FiberNode(tag, pendingProps, key, mode);
}

createFiber is then called either directly or transitively by so many other functions in the whole codebase.

Here are some of the functions that will create fibers directly:


// this function is called from prepareFreshStack and while rendering some
// component tags such as MemoComponent or suspense fallback. Or when
// cloning child fibers (cloneChildFibers) which is called when bailout
// on finished work is called. This function is also called from useFiber
// during reconciliation.
function createWorkInProgress(current, pendingProps) {}


// this function is called from createRoot
function createHostRootFiber(tag, isStrictMode, concurrentUpdatesByDefaultOverride) {}


// called from createFiberFromElement(element, mode, lanes) during reconciliation
// and when rendering memo components
function createFiberFromTypeAndProps(
type: any, // the component
key: null | string, // key
pendingProps: any, // nextProps
source, // used in dev mode, contains the fileName and lineNumber
owner: null | Fiber, // used in dev mode, a fiber
mode: TypeOfMode, //
lanes: Lanes //
) {}


// the following calls occurs from within the function createFiberFromTypeAndProps
// defined in ReactFiber module.
// it serves as a dispatchers to the real creation per tag.
// React uses so many functions to avoid any wrong abstraction or possible
// bugs due to several clashing cases.
// <Explicit code is the best code>
function createFiberFromFragment(elements, mode, lanes, key) {}
function createFiberFromScope(scope, pendingProps, mode, lanes, key) {}
function createFiberFromProfiler(pendingProps, mode, lanes, key) {}
function createFiberFromSuspense(pendingProps, mode, lanes, key) {}
function createFiberFromSuspenseList(pendingProps, mode, lanes, key) {}
function createFiberFromOffscreen(pendingProps, mode, lanes, key) {}
function createFiberFromLegacyHidden(pendingProps, mode, lanes, key) {}
function createFiberFromCache(pendingProps, mode, lanes, key) {}
function createFiberFromTracingMarker(pendingProps, mode, lanes, key) {}
function createFiberFromText(content, mode, lanes) {}
function createFiberFromHostInstanceForDeletion() {}
function createFiberFromDehydratedFragment(dehydratedNode) {}
function createFiberFromText(content, mode, lanes) {}
function createFiberFromPortal(portal, mode, lanes) {}
function createFiberFromPortal(portal, mode, lanes) {}

// All the previous functions have a similar shape than this one:
function createSpecialFiber(
pendingProps,
mode,
lanes,
key
) {
const fiber = createFiber(SpecialTag, pendingProps, key, mode);
// element type when assigned to a react internal value means that it is
// for internal usage only and that it may be exported as a component
// such Suspense...
fiber.elementType = REACT_SPECIAL_ELEMENT_TYPE; // or component type
fiber.lanes = lanes;
}

The fibers instantiation will initialize all the attributes a fiber uses and that's it.