Understanding React - Part 1. React Element, JSX and Fiber

Understanding React - Part 1. React Element, JSX and Fiber
July 6
# Tech
# Front-End
# React

Introduction

Welcome to the first installment of our series on Understanding React. In this post, we'll dive into the fundamental building blocks of React: React Elements, JSX, and Fiber. Whether you're new to React or looking to deepen your understanding, these concepts are essential for every front-end developer.

In essence, JSX defines the UI, which is then translated into a React Element, and ultimately into a Fiber. Fiber stores all the metadata about an element in React, including state, props, and more. As the minimal operational unit in React, Fiber is a crucial concept that underlies all higher-level operations.

Let’s start with JSX.

JSX

JSX, short for JavaScript XML, is a syntax extension for JavaScript that allows you to write HTML-like code directly within your JavaScript files. It provides a more intuitive and declarative way to define UI components in React.

Here’s an example:

Demo JSX

JSX isn’t directly understood by browsers. Instead, it’s transpiled into regular JavaScript functions using tools like @babel/plugin-transform-react-jsx. You can see this in action on the Babel REPL playground

For example, the JSX code above is compiled to:

JSX Transform

As you can see, the JSX is translated into a function call, specifically jsx. This function is responsible for creating a React Element.

In React Rebuild, I won't achieve the transpiler @babel/plugin-transform-react-jsx since this transpiler is not the core function in React. Let's focus on what the jsx function does, here is the code:

JSX Fucntion

There are three params:

  1. type: The type of this Element.
    1. For HTML tags, the type will be the name of the tag, like div.
    2. For function component, the type will be a reference to the function itself, like jsx(FunctionComponent, props, key)
    3. For class component, the type will be a reference to the class constructor, like jsx(ClassComponent, props, key)
    4. For Fragment, the type will be the Fragment exported by react, like jsx(_Fragment, props, key)
  2. config: The props of this Element, like children, ref, classname, and more.
  3. key: The key of this Element. If you want to learn more about why the react team decided to pass key separately from props, you can read this RFC.

Finally, ReactElement(type, key, ref, props) is returned. What is ReactElement? Let's move on to the next section.

React Element

React Elements are the smallest building blocks of React applications. They are plain objects that represent the UI at a particular point in time. Unlike DOM elements, React Elements are immutable and lightweight.

Here is a React Element like:

React Element

A React Element mainly has these properties (without some dev-only props):

  1. $$typeof: Annotation that marks the object as a React Element. We use this to determine if an object is a React Element.
  2. type: The type of the element. This can be a string (for built-in components) a class/function (for composite components) or a symbol (for fragments).
  3. props: The props of the element.
  4. key: The key of the element.
  5. ref: The ref of the element.

There are two ways to create a React Element: using JSX, which we discussed earlier, or by directly calling React.createElement. Before React 17, when transforming JSX into React Element, React.createElement is called behind the scene. That's why you have to add import React from "react" wherever you using JSX, or the createElement will not be found. But after React 17, the jsx function is used to transform JSX into React, and the transformer, like Babel, will auto-import it from react/jsx-runtime. If you are interested in this change, you can read this RFC

Using JSX

For JSX, we mentioned that ReactElement(type, key, ref, props) is returned at the end of the function jsx. And ReactElement function assembles these essential params and returns a React Element, here is the code:

JSX ReactElement

Using React.createElement

Here's an example using React.createElement:

React.createElement

In this example, we create a React Element using React.createElement. The first argument is the type of element (in this case, a div), the second argument is an object containing the props, and the third argument is the child element (the text Hello World!). And the element is equal to:

React Element

Regardless of the method, the resulting React Element is used to create a Fiber. Let's move on to the next section.

Fiber

Fiber is a reimplementation of React’s core algorithm for rendering and reconciliation. It is designed to enable more granular control over rendering and prioritization of updates.

Fiber's key role is in the reconciliation process, where React compares the previous and current states of components to determine what has changed. This process allows React to optimize rendering performance by scheduling and executing updates more efficiently.

Fiber nodes represent individual units of work that React can pause, abort, or prioritize based on changes in state or props.

Let's dive into the internal of a Fiber node:

Fiber

There are many important properties in a fiber node:

  • key: The unique identifier for this fiber.
  • ref: Holding a reference to a DOM element or a React component instance. This allows us to interact directly with the underlying DOM nodes or React components.
  • type: the type of component or element this fiber represents, like the first param in the jsx function.
    • Some possible values:
      • For FunctionComponent: a reference to the function itself.
      • For ClassComponent: a reference to the class constructor.
      • For HostComponent: a string corresponding to the HTML tag name (e.g., div, span, button)
      • For HostText: null.
      • For Fragment: React.Fragment exported by react, a symbol.
  • tag: The tag of the fiber represents.
    • Some possible values:
      • HostRoot: represents the root of the fiber tree.
      • FunctionComponent: represents a function component.
      • ClassComponent: represents a class component.
      • HostComponent: represents an element in the host environment.
      • HostText: represents a text node.
      • Fragment: represents a React Fragment.
  • stateNode: a direct reference to the actual instances (DOM nodes, component instances, or root containers) associated with this fiber.
    • Some possible values:
      • For HostComponent: a reference to the corresponding DOM node.
      • For ClassComponent: a reference to the instance of the component created by the constructor.
      • For FunctionComponent: typically null because function components do not have instances.
      • For HostRoot: a reference to the Fiber Root, which contains the reference to the root container where the React tree is mounted.
  • return: points to the parent fiber.
  • child: points to the first child fiber.
  • sibling: points to the next fiber in the list of siblings.
  • index: the index of this fiber in the list of siblings.
  • pendingProps: represents the next set of props that a component will receive.
  • memorizedProps: represents the props that were last applied to the component after the reconciliation process is completed.
  • memorizedState: represents the internal state of a component.
    • Some possible values:
      • HostRoot: the element that is rendered in the root container
      • FunctionComponent: the first hook in the linked list of hooks that store the component's state
  • updateQueue: the queue of updates that need to be applied to this fiber.
    • For function components, the update queue is a linked list of effects that need to be applied to the component.
  • alternate: links a fiber to its previous/next version (work-in-progress -> current -> work-in-progress), enabling efficient reconciliation between renders.
  • flags: a collection of flags that represent the current state of the fiber
  • subtreeFlags: a collection of flags that represent the state of the subtree rooted at this fiber
  • deletions: a collection of fibers that represent the children that were deleted from the tree during the current update

Except for the first three properties that we have learned in the previous section, many new concepts are popping up. Don't worry, we will use these properties in the following learning and will have a better understanding of them. For now, we can simply create a fiber using a react element like:

Create a Fiber

This creates our first Fiber node based on the JSX, taking us one step further into understanding React.

A Fiber created from React Element

In the End

In this post, we've covered the basics of React: JSX, React Element, and Fiber. We've seen how JSX is compiled into React Elements, which are then used to create Fibers. These concepts form the foundation of React and are essential for building robust and efficient applications.

Stay tuned for the next installment, where we’ll dive deeper into React by introducing the three main stages: scheduling, rendering, and committing changes. We’ll explore how these stages work together to manage updates and ensure smooth user experiences.

Related Reads