Github address: github.com/RainyNight9…

2. Fiber

2.1, the react diff

Reconciliation coordination

The motivation

At some point a node calls the Render () method of React, creating a tree of React elements. The same render() method returns a different tree the next time state or props is updated. React needs to determine how to update the UI efficiently based on the differences between the two trees to keep the current UI in sync with the latest tree.

Some common solutions to this algorithm are to generate a minimum number of operations to convert one tree to another. However, even with the optimal algorithm, the complexity of the algorithm is O(n 3), where n is the number of elements in the tree.

If you use this algorithm in React, showing 1000 elements would require a billion comparisons. The cost is simply too high. React proposed a set of O(n) heuristic algorithm based on the following two assumptions:

1. Two different types of elements produce different trees; 2. The developer can set the key property to tell the render which child elements can be saved in different renders.Copy the code

In practice, we find that the above assumptions hold true in almost all practical scenarios.

Diffing algorithm

Algorithm complexity O(n)

The diff strategy

1. Compared with the peer level, DOM nodes in The Web UI have very few cross-level movement operations, which can be ignored. 2. Having two components of different types will generate different tree structures. 3. Developers can use key prop to indicate which child elements are stable in different renders.Copy the code

The diff process

When comparing two virtual DOM’s, there are three operations: delete, replace, and update.

Vnode is the current virtual DOM, and newVnode is the new virtual DOM.

* Delete: newVnode does not exist * Replace: vnode and newVnode have different types or keys * Update: vnode and newVnode have the same types and keys, but vnode and newVnode have different keysCopy the code

In practice, the three premise strategies are proved to be reasonable and accurate, which guarantee the performance of the whole interface construction.

2.2 fiber source code

What is still followed? Why is that? How to do? Answer the three steps:

what

The literal meaning is fiber, but also a semantic word of the name, you product, you fine product.

.

Forget it, I’ll leave you alone…

Fiber means “fibrosis” in English, which means refinement, which refines tasks. We can divide a long task into many small pieces, each of which has a short running time. Although the total time is still very long, other tasks are given a chance to execute after each small piece is executed, so that the only thread is not monopolized and other tasks still have a chance to run.

The official one-sentence explanation is that “React Fiber is a re-implementation of the core algorithm.”

why

For large projects, the component tree will be very large, so the cost of recursion will be very high, which will cause the main thread to be continuously occupied. As a result, the periodic tasks such as layout and animation on the main thread cannot be processed immediately, resulting in visual lag and affecting user experience.

where

2. Incremental rendering, dividing the rendering task into blocks and dividing it into multiple frames. 3, update can suspend, terminate, reuse rendering tasks. 4. Assign priority to different types of updates, and the one with the highest priority will be executed first. 5. New basic capabilities in concurrency. 6. Be more fluidCopy the code

Fiber is the data type of fiber.

// A Fiber is work on a Component that needs to be done or was done. There can // be more than one per component. export  type Fiber = {| // Tag identifying the type of fiber. tag: WorkTag, // Unique identifier of this child. key: null | string, // The value of element.type which is used to preserve the identity during // reconciliation of this child. elementType:  any, // The resolved function/class/ associated with this fiber. type: any, // The local state associated with this fiber. stateNode: any, // Conceptual aliases // parent : Instance -> return The parent happens to be the same as the // return fiber since we've merged the fiber and instance. // Remaining fields belong to Fiber // The Fiber to return to after finishing processing this one. // This is effectively the parent, but there can be multiple parents (two) // so this is only the parent of the thing we're currently processing. // It is conceptually the same as the return address of a stack frame. return: Fiber | null, // Singly Linked List Tree Structure. child: Fiber | null, sibling: Fiber | null, index: number, // The ref last used to attach this node. // I'll avoid adding an owner field for prod and model that as functions. ref:  | null | (((handle: mixed) => void) & {_stringRef: ? string, ... }) | RefObject, // Input is the data coming into process this fiber. Arguments. Props. pendingProps: any, // This type will be more specific once we overload the tag. memoizedProps: any, // The props used to create the output. // A queue of state updates and callbacks. updateQueue: mixed, // The state used to create the output memoizedState: any, // Dependencies (contexts, events) for this fiber, if it has any dependencies: Dependencies | null, // Bitfield that describes properties about the fiber and its subtree. E.g. // the ConcurrentMode flag indicates whether  the subtree should be async-by- // default. When a fiber is created, it inherits the mode of its // parent. Additional flags can be set at creation time, but after that the // value should remain unchanged throughout the fiber's lifetime, particularly // before its child fibers are created. mode: TypeOfMode, // Effect flags: Flags, subtreeFlags: Flags, deletions: Array<Fiber> | null, // Singly linked list fast path to the next fiber with side-effects. nextEffect: Fiber | null, // The first and last fiber with side-effect within this subtree. This allows // us to reuse a slice of the linked list when we reuse the work done within // this fiber. firstEffect: Fiber | null, lastEffect: Fiber | null, lanes: Lanes, childLanes: Lanes, // This is a pooled version of a Fiber. Every fiber that gets updated will // eventually have a pair. There are cases when we can clean up pairs to save // memory if we need to. alternate: Fiber | null, // Time spent rendering this Fiber and its descendants for the current update. // This tells us how well the tree makes use of sCU for memoization. // It is reset to 0 each time we render and only updated when we don't bailout. // This field is only set when the enableProfilerTimer flag is enabled. actualDuration? : number, // If the Fiber is currently active in the "render" phase, // This marks the time at which the work began. // This field is only set when the enableProfilerTimer flag is enabled. actualStartTime? : number, // Duration of the most recent render time for this Fiber. // This value is not updated when we bailout for memoization purposes. // This field is only set when the enableProfilerTimer flag is enabled. selfBaseDuration? : number, // Sum of base times for all descendants of this Fiber. // This value bubbles up during the "complete" phase. // This field is only set when the enableProfilerTimer flag is enabled. treeBaseDuration? : number, // Conceptual aliases // workInProgress : Fiber -> alternate The alternate used for reuse happens // to be the same as work in progress. // __DEV__ only _debugID? : number, _debugSource? : Source | null, _debugOwner? : Fiber | null, _debugIsCurrentlyTiming? : boolean, _debugNeedsRemount? : boolean, // Used to verify that the order of hooks does not change between renders. _debugHookTypes? : Array<HookType> | null, |};Copy the code

The fiber source code is in the Packages/React-Reconciler directory, which is not easy to paste here. You can download the source code for yourself.

Realize the fiber

MDN is described as follows:

window.requestIdleCallback(callback[, options])
Copy the code

Window. RequestIdleCallback () method will be called function in browser free time line. This enables developers to perform background and low-priority work on the main event loop without affecting the delay of critical events such as animations and input responses. Functions are typically executed in first-come-first-called order; however, if a callback function specifies a timeout, it is possible to scramble the order of execution in order to execute the function before it times out.

You can call requestIdleCallback() in the idle callback function to schedule another callback before passing through the event loop the next time.

callback

A reference to a function to be called when the event loop is idle. The function receives a parameter called IdleDeadline, which gets the status of the current idle time and whether the callback has been executed before the timeout.

The options of the optional

Includes optional configuration parameters. It has the following attributes:

Timeout: If timeout is specified with a positive value, and the callback has not been invoked by timeout milliseconds, the callback will be enforced in the next idle period, even though this will most likely have a negative impact on performance.Copy the code

2.3. Realize Fiber architecture myReact

No nonsense, directly on the code, but the code needs a good look step by step, product product.

Make a copy of the myReact project code and make some changes.

SRC /myreact2/react-dom.js

// vNode virtual DOM object // node real DOM node //! Let wipRoot = null; Function render(vnode, container) {// // react17 // console.log("vnode", vnode); // // vnode->node // const node = createNode(vnode); // // node->container // container.appendChild(node); wipRoot = { type: 'div', props: { children: {... vnode}, }, stateNode: container, }; nextUnitOfWOrk = wipRoot; Function createNode(workInProgress) {const {type} = workInProgress; const {type} = workInProgress; const node = document.createElement(type) updateNode(node, workInProgress.props) return node; Fiber function updateHostComponent(workInProgress) {const {type, props} = workInProgress; fiber function updateHostComponent(workInProgress) {const {type, props} = workInProgress; if(! StateNode) {workinprogress.statenode = createNode(workInProgress)} // Harmonize reconcileChildren(workInProgress, props.children); // Children console.log('workInProgress', workInProgress)} nextVal) { Object.keys(nextVal) // .filter((k) => k ! ForEach ((k) => {console.log('k', k, nextVal[k], Array.isArray(nextVal[k])) if (k === 'children') { if (typeof nextVal[k] === "string") { node.textContent = nextVal[k]; } else if (Array.isArray(nextVal[k]) && typeof nextVal[k][0] === "string") { console.log('nextVal[k]? .join()', nextVal[k]? .join()) node.textContent = nextVal[k]? .join('') } } else { node[k] = nextVal[k] } }); Function updateTextComponent(vNode) {const node = document.createTextNode(vnode); const node = document.createTextNode(vnode); return node; Function updateFunctionComponent(workInProgress) {const {type, props} = workInProgress; function updateFunctionComponent(workInProgress) {const {type, props} = workInProgress; const child = type(props); reconcileChildren(workInProgress, child); Function updateClassComponent(workInProgress) {const {type, props} = workInProgress; // Class components need new const instance = new type(props); console.log('instance', instance); const child = instance.render(); reconcileChildren(workInProgress, child); ReconcileChildren (workInProgress, reconcileChildren); // reconcileChildren(workInProgress, reconcileChildren); // a text or a number generates fiber, As an attribute if (typeof children = = = "string" | | typeof children = = = "number") {return; } const newChildren = array.isarray (children)? children : [children]; Fiber let previousNewFiber = null; for (let i = 0; i < newChildren.length; i++) { let child = newChildren[i]; NewFiber = {key: child.key, type: child.type, props: {... child.props}, stateNode: null, child: null, sibling: null, return: workInProgress, }; If (I === 0) {// First subfiber workinprogress. child = newFiber; } else { previousNewFiber.sibling = newFiber; PreviousNewFiber = newFiber; Fiber let nextUnitOfWOrk = null; fiber let nextUnitOfWOrk = null; // Fiber js object // type // key // props // stateNode // Child // Sibling next sibling // return parent // 2-- Next task Function performUnitOfWork(workInProgress) {// const {type} = workInProgress; If (typeof Type === "string") {// updateHostComponent(workInProgress); } else if (typeof type === "function") { type.prototype.isReactComponent ? updateClassComponent(workInProgress) : updateFunctionComponent(workInProgress); If (workinprogress.child) {return workinprogress.child; } // Let nextFiber = workInProgress; While (nextFiber) {if (nextfibre.sibling) {return nextfibre.sibling; } nextFiber = nextFiber.return; }} // 2-- function workLoop(IdleDeadline) {// Returns a time DOMHighResTimeStamp, and is a floating point value, which represents the estimated number of milliseconds remaining in the current idle period. // If idle period has ended, its value is 0. // Your callback function (passed to requestIdleCallback) can repeatedly access this property to determine whether the idle time of the current thread can perform more tasks before ending. While (nextUnitOfWOrk && IdleDeadline. TimeRemaining () > 1) {/ /, NextUnitOfWOrk = performUnitOfWork(nextUnitOfWOrk); } // submit if (! nextUnitOfWOrk && wipRoot) { commitRoot(); }} // HTTP: / / https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestIdleCallback / / 2 - in the browser's free time called function to wait in line requestIdleCallback(workLoop); Function commitRoot() {commitWorker(wiproot.child); wipRoot = null; } // 2-- commitWorker(workInProgress) {if (! workInProgress) { return; } // parentNode dom node //? Fiber let parentNodeFiber = workinprogress.return; // All fiber nodes have DOM nodes. // fiber // Some nodes are while (! parentNodeFiber.stateNode) { parentNodeFiber = parentNodeFiber.return; } / / write flashbacks, the second step Let the parent node of the dom node parentNode = parentNodeFiber. StateNode; // add if (workinprogress.statenode) {parentNode.appendChild(workinprogress.statenode); } // commitWorker(workinprogress.child); // commitWorker(workinprogress.sibling); } export default { render };Copy the code

3. Hooks

3.1. Read Hooks

Hook profile

Hook is a new feature in React 16.8. It lets you use state and other React features without having to write a class.

1. What are Hooks? Function components have states and other React features instead of classes, in order to embrace the changes brought about by Hooks of function 2Copy the code
import React, { useState } from 'react'; Const [count, setCount] = useState(0); function Example() {const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }Copy the code

No destructive changes

Before we continue, remember that a Hook is:

* Completely optional. You can try hooks in some components without rewriting any existing code. But you don't have to learn or Hook now if you don't want to. * 100% backward compatible. Hooks do not contain any destructive changes. * Now available. Hook was released in V16.8.0.Copy the code

There are no plans to remove classes from React.

Hook doesn’t affect your understanding of React concepts. Instead, hooks provide a more straightforward API for known React concepts: props, state, Context, refs, and lifecycle.

What problem do Hooks solve?

Hooks solve all sorts of seemingly unrelated problems we’ve encountered over the past five years writing and maintaining thousands of components. Whether you’re learning React, using it every day, or would rather try another framework that has a similar component model to React, you’re probably familiar with these issues.

Reusing state logic between components is difficult

React does not provide a way to “attach” reusability behavior to components (for example, to connect components to stores). If you’ve worked with React for a while, you’re probably familiar with some of the solutions to this problem, such as render props and higher-order components. But such scenarios require reorganizing your component structure, which can be cumbersome and make your code difficult to understand. If you look at React apps in React DevTools, you’ll see that components made up of providers, consumers, high-order components, render props, and other abstraction layers form a nested hell. Although we can filter them out in DevTools, this illustrates a deeper problem: React needs to provide a better native way to share state logic.

You can use hooks to extract state logic from components so that it can be individually tested and reused. Hooks allow you to reuse state logic without modifying the component structure. This makes it easier to share hooks between components or within a community.

Complex components become difficult to understand

We often maintain components that start out simple, but gradually become overwhelmed with state logic and side effects. Each life cycle often contains some unrelated logic. For example, components often get data in componentDidMount and componentDidUpdate. However, the same componentDidMount may also contain a lot of other logic, such as setting up event listeners that need to be cleared later in componentWillUnmount. Code that is related and needs to be modified against each other is split, while completely unrelated code is grouped together in the same method. This is very buggy and leads to logical inconsistencies.

In most cases, it is not possible to break components down into smaller granularity because state logic is ubiquitous. This makes testing a bit of a challenge. This is also one of the reasons many people use React in conjunction with the state management library. However, this often introduces a lot of abstraction and requires you to switch back and forth between different files, making reuse more difficult.

To solve this problem, hooks break the interrelated parts of a component into smaller functions (such as setting up subscriptions or requesting data) rather than enforcing a lifecycle partition. You can also use Reducer to manage the internal state of components and make them more predictable.

Hard to understand class

In addition to the difficulties of code reuse and code management, we found class to be a big barrier to learning React. You have to understand how this works in JavaScript, which is vastly different from other languages. And don’t forget to bind event handlers. Without a stable syntax proposal, the code is very redundant. You can understand props, state, and the top-down data flow pretty well, but you can’t do anything about class. Even experienced React developers disagree about the difference between a function component and a class component, and even how the two components are used.

React has been around for five years, and we expect it to be around for the next five years. As other libraries like Svelte, Angular, Glimmer and others show, component precompilation offers great potential. Especially if it’s not limited to templates. Recently, we’ve been experimenting with Component Folding using Prepack with some success. However, we found that using class components inadvertently encouraged developers to use solutions that made optimizations ineffective. Class also poses some problems for current tools. For example, class does not compress well and can make thermal overload unstable. Therefore, we want to provide an API that makes the code easier to optimize.

To address these issues, hooks allow you to use more React features in non-class situations. Conceptually, the React component has always been more like a function. Hook embraced functions without sacrificing the spirit of React. Hooks provide solutions to problems without learning complex functional or reactive programming techniques.

Principle of Hooks

function FunctionalComponent () {
    const [state1, setState1] = useState(1)
    const [state2, setState2] = useState(2)
    const [state3, setState3] = useState(3)
}

hook1 => Fiber.memoizedState
state1 === hook1.memoizedState

hook1.next => hook2
state2 === hook2.memoizedState

hook2.next => hook3
state3 === hook3.memoizedState
Copy the code

Reactfiberrest.old.js. Download the reactFiberRest.old.js code from reactFiberRest.old.js.