Their “self-introductions.”
React Element
React Element
is the smallest unit for building a React application. It is a lightweight description of a DOM element that ends up being transformed into a pure JavaScript object. The React Element
object contains a few basic attributes such as:
type: the type of the element, either a string (e.g. ‘div’, ‘span’, etc. HTML elements) or a React component.
props: contains the attributes of the element, as well as the data passed to the child element.
key: an optional string that must be unique between sibling elements, used to help identify the element’s stability during re-rendering.
React Element
is immutable; once created, you can’t change its contents or properties. If the interface needs to be updated, React creates a new Element and efficiently updates the DOM by comparing the old and the new Elements
if necessary. Therefore, you can think of React Element
as pure data structures describing the structure of the interface, which are the components of the virtual DOM that are used to ultimately generate the real DOM structure. This design allows React to efficiently update the interface by comparing and re-rendering Elements
without directly manipulating the DOM.
// <div classname="snail-run">snailRun</div>;
{
'$$typeof': Symbol(react.transitional.element),
type: 'div',
key: null,
props: { classname: 'snail-run', children: 'snailRun' },
_owner: null,
_store: {}
}
React Component
React Component
are separate, reusable chunks of code that make up a React application. They are essentially JavaScript
functions or classes that return React Element
. Components can take input (called props
) and return React Element
. There are two types of components: function components and class components. Function components are generally cleaner and support hooks, and they can also be the first argument to React.createElement
, which is the type field. In our day-to-day development of React projects, the most common component we write is React Component
, so we’ll write about the simplest function and class components:
function AppFunc() {
return <div>snailRun</div>;
}
class AppClass extends React.Component {
render() {
return <div>snailRun</div>;
}
}
JSX
JSX is a syntax extension (syntactic sugar) that looks a lot like XML
or HTML
. JSX
It provides a more intuitive way to describe the UI, and it allows developers to write tag language in JavaScript
code. During compilation, JSX
is converted to the standard JavaScript
object, React Elements
, the JSX example code:
<div classname="name">snailRun</div>
Fiber
Fiber
is a new internal architecture for enhancing React’s capabilities, particularly in animation, layout, and interrupted rendering. Fiber
First introduced at React 16
, the architecture addresses the problem of unbreakable recursive updates in previous versions of React by breaking up rendering into smaller units, and after each small portion of work, handing control back to the browser to let the browser handle the rest of the work, such as animation, layout, and input response. This ability is called “interruptible rendering” Fiber, as a static data structure, stores a lot of information, much like a JSX data structure, but it stores a lot more. It’s essentially an abstraction of a unit of work that represents what React needs to do to build and update the DOM. Each React Element
corresponds to a Fiber node, and the structure of the application can be thought of as a giant Fiber tree.
Diff Algorithm
When a component’s state or properties change, React needs to decide whether to update the DOM or not. React uses the **Diff algorithm** to compare the old and new fiber trees and mark and prioritize the Fiber nodes that need to be added, deleted, or updated, and then commits the changes to the DOM, a process called Reconciliation
(orchestration).
The Diff algorithm identifies the parts that need to be updated and generates the appropriate actions to update the DOM.This approach ensures that only the parts that have actually changed are re-rendered, optimizing performance.
Trivia >> The difference between first-screen rendering and updating is whether or not there is a diff algorithm in the process of creating the fiber tree
Look at the source code.
Let’s take a look at the compilation result of JSX. First of all, let’s write a JSX code without adding any plugin, we can see that the error is reported, because originally JavaScript
doesn’t know how to compile JSX.
We’ll add a transform-react-jsx
plugin at this point by sliding left to the bottom and clicking on add plugin
.
We can see that the JSX syntax was successfully compiled, and our left side was compiled to become the right side, which results in the JSX result:
So let’s now see what the result of compiling this code is? That is, what does React.createElement
do? I helped you find the implementation of React.createElement
in the React source code, which is the following code:
/**
* Create and return a new ReactElement of the given type.
* See https://reactjs.org/docs/react-api.html#createelement
*/
export function createElement(type, config, children) {
if (__DEV__) {
if (!isValidElementType(type)) {
// This is an invalid element type.
//
// We warn in this case but don't throw. We expect the element creation to
// succeed and there will likely be errors in render.
let info = '';
if (
type === undefined ||
(typeof type === 'object' &&
type !== null &&
Object.keys(type).length === 0)
) {
info +=
' You likely forgot to export your component from the file ' +
"it's defined in, or you might have mixed up default and named imports.";
}
let typeString;
if (type === null) {
typeString = 'null';
} else if (isArray(type)) {
typeString = 'array';
} else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {
typeString = `<${getComponentNameFromType(type.type) || 'Unknown'} />`;
info =
' Did you accidentally export a JSX literal instead of a component?';
} else {
typeString = typeof type;
}
console.error(
'React.createElement: type is invalid -- expected a string (for ' +
'built-in components) or a class/function (for composite ' +
'components) but got: %s.%s',
typeString,
info,
);
} else {
// This is a valid element type.
// Skip key warning if the type isn't valid since our key validation logic
// doesn't expect a non-string/function type and can throw confusing
// errors. We don't want exception behavior to differ between dev and
// prod. (Rendering will throw with a helpful message and as soon as the
// type is fixed, the key warnings will appear.)
for (let i = 2; i < arguments.length; i++) {
validateChildKeys(arguments[i], type);
}
}
// Unlike the jsx() runtime, createElement() doesn't warn about key spread.
}
let propName;
// Reserved names are extracted
const props = {};
let key = null;
let ref = null;
if (config != null) {
if (__DEV__) {
if (
!didWarnAboutOldJSXRuntime &&
'__self' in config &&
// Do not assume this is the result of an oudated JSX transform if key
// is present, because the modern JSX transform sometimes outputs
// createElement to preserve precedence between a static key and a
// spread key. To avoid false positive warnings, we never warn if
// there's a key.
!('key' in config)
) {
didWarnAboutOldJSXRuntime = true;
console.warn(
'Your app (or one of its dependencies) is using an outdated JSX ' +
'transform. Update to the modern JSX transform for ' +
'faster performance: https://react.dev/link/new-jsx-transform',
);
}
}
if (hasValidRef(config)) {
if (!enableRefAsProp) {
ref = config.ref;
if (!disableStringRefs) {
ref = coerceStringRef(ref, getOwner(), type);
}
}
if (__DEV__ && !disableStringRefs) {
warnIfStringRefCannotBeAutoConverted(config, config.__self);
}
}
if (hasValidKey(config)) {
if (__DEV__) {
checkKeyStringCoercion(config.key);
}
key = '' + config.key;
}
// Remaining properties are added to a new props object
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
// Skip over reserved prop names
propName !== 'key' &&
(enableRefAsProp || propName !== 'ref') &&
// Even though we don't use these anymore in the runtime, we don't want
// them to appear as props, so in createElement we filter them out.
// We don't have to do this in the jsx() runtime because the jsx()
// transform never passed these as props; it used separate arguments.
propName !== '__self' &&
propName !== '__source'
) {
if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
props.ref = coerceStringRef(config[propName], getOwner(), type);
} else {
props[propName] = config[propName];
}
}
}
}
// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}
// Resolve default props
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (__DEV__) {
if (key || (!enableRefAsProp && ref)) {
const displayName =
typeof type === 'function'
? type.displayName || type.name || 'Unknown'
: type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (!enableRefAsProp && ref) {
defineRefPropWarningGetter(props, displayName);
}
}
}
const element = ReactElement(
type,
key,
ref,
undefined,
undefined,
getOwner(),
props,
);
if (type === REACT_FRAGMENT_TYPE) {
validateFragmentProps(element);
}
return element;
}
Now let me briefly explain the code:
Parameter Description:
type: the type of the element, either a string of HTML tags or a React component (function components and class components)
config: a configuration object containing the properties (props) of the element, possibly including special properties such as key and ref.
children: child elements, which can be any number of arguments indicating the child nodes of the element.
Example: React.createElement("div", {classname: "snail-run"}, "snailRun");
Primary Logic:
Type validation: first check if type is valid. If it is not valid, an error message will be output on the console. The validity check includes whether it is undefined, whether it is an empty object, whether it is an array, and so on.Handles configuration objects (config):
Handle ref: if there is a ref attribute in the configuration object and the current environment allows ref as an attribute, then add it to the new props object.
Handling key: If there is a key attribute in the configuration object, convert it to a string and store it.
Copy Other Properties: Copies other properties from theconfig
object to the new props object, ignoring special or reserved properties such askey、ref、__self
and__source
.
Handles child elements:
If there is only one child element, assign it directly toprops.children
.
If there are multiple child elements, put them into an array and assign them toprops.children
.
Handling default attributes: If type type has a default attribute (defaultProps
), populate attributes not explicitly set in props with the default value.
Creating a React Element: Use theReactElement
function to create a new React element, passing in parameters such astype、key、ref、props
.
Special type handling: if type isFragment
, the attributes ofFragment
will also be validated.
Differences between development and production environments
The conditional compilation directive at __DEV__
appears several times in the code, which is used to distinguish between development and production environments. In the development environment, more warning and error checking is done to help developers identify potential problems. Summary:
createElement
The function accepts the arguments, checks them and processes them, and finally returnsReactElement
, which means that JSX compiles the result toReactElement
.
**JSX ** is the syntactic sugar used to declare elements when writing React components, essentiallyReact.createElement
, and eventually JSX is converted toReact Element
.
Their relationship
JSX’s relationship to React Element:JSX
is a syntactic extension ofJavaScript
, which looks a lot likeHTML
. Developers usually useJSX
to describe theUI
structure. WhenJSX
is compiled, it is converted toReact Element
. Therefore, you can think ofJSX
as the syntactic sugar that createsReact Element
.
React Element vs. React Component:React Element
is the smallest building block in aReact
application, and it is a lightweight description of the component’s output.React Component
In turn, it is a separate unit that encapsulates the logic and state that returnsReact Element
. Thus, a component is a container for creating and managing elements.
The relationship between React Element and Fiber nodes: eachReact Element
corresponds internally to aFiber
node, which are the units that actually perform the work.
Relationship between React Component and Fiber: TheFiber
architecture, a new orchestration algorithm introduced in theReact 16
release, is used to improve the performance and responsiveness of applications. EachReact Component
corresponds to one or moreFiber
nodes at render time.React
A unit of work in theFiber
node is typically a node, andReact
builds and updates the virtualDOM
through these units of work.
Diff Algorithm Compare Element Changes: When a component’s state or attributes change,React
uses theDiff
algorithm to compare the old with the newReact Element
to determine what needs to be updated.
Fiber’s relationship to the Diff algorithm:Diff
The algorithm is the process used inReact
to compare the differences between the old and new virtual DOMs and determine how to effectively update the real DOM. In theFiber
architecture, theDiff
algorithm is used to determine whichFiber
nodes need to be changed and which can be preserved.Fiber
The architecture allowsReact
to interrupt and resume theDiff
process, allowing tasks to be prioritized to optimize performance.
Overall process: When the state or properties of a component change,React
re-executes the render function of the component (React Component
) created byJSX
(React.createElement
syntactic sugar) to generate a newReact Element
. Then,React
uses the Diff algorithm to compare the old and new elements, breaks down the changes into small tasks via the **Fiber architecture**, executes these tasks incrementally, generates a newfiber
tree, and finally reflects the changes to the realDOM
.
To summarize, JSX provides a declarative syntax for generating React Element / React Component
(function components or class components), the elements that are exported from React Component
. React Component
Their lifecycle and state changes are managed through the Fiber schema, while the Fiber
schema internally uses the **Diff algorithm** to optimize the update process. These concepts work together to allow React
to build user interfaces efficiently and flexibly.