Updated irregularly 2021.08.03

JSX

What is JSX?

JSX is a syntactic extension of JavaScript that is close to the template language, but has full JavaScript capabilities.

class App extends React.Component { ... render() { return ( <div id="li"> Hello <p>world</p> </div> ); }}Copy the code

The content of the return in the component is JSX

Do browsers naturally support JSX?

Unsupported Babel translates JSX into a function call called react.createElement () that generates the corresponding DOM

class App extends React.Component { render() { return React.createElement( "div", { id: "li" }, "Hello", React.createElement( "p", null, "world" ) ); }}Copy the code

Virtual DOM

What is the virtual DOM?

  • Is essentially a mapped cache between JS and DOM
  • Morphologically, it is a JS object that describes the DOM structure and its attribute information

How does the virtual DOM work in general

  • Mount the stage

React will build a virtual DOM tree with JSX’s description, and then map the virtual DOM to the real DOM using reactdom.render.

  • Update the stage

Page changes are applied to the virtual DOM before they are applied to the real DOM. The virtual DOM uses algorithms in the JS layer to compare specific real DOM changes that need to be made, and then these changes are applied to the real DOM

Virtual DOM workflow

Both diff and Patch are function names that are derived from a separate virtual DOM library

Like Redux, the virtual DOM is not tied to any specific framework; That said, React isn’t the only thing with the virtual DOM

Does the virtual DOM really perform better

The ability to provide a cleaner, more efficient mode of development (i.e., functional UI programming) while still maintaining a decent performance advantage is that there is less manipulation of the real DOM in the form of patches

But this is not absolute.

The construction of a virtual DOM and diff process logic is relatively complicated, it inevitably involve recursive, traverse the time-consuming operation, etc If the data content change is very large (or changed), prompting delta updated calculated results and the whole amount is very close to (or identical), instead of using the virtual DOM would increase costs

But that is unlikely.

Because the disadvantage of virtual DOM mainly lies in the time consuming of JS computation, and the energy consumption of DOM operation and JS calculation are not of the same magnitude at all, the performance consumption of a very small amount of DOM operation is enough to support a large number of JS computation

Is the value of the virtual DOM in its performance

There are two key problems that the virtual DOM solves:

  1. The ISSUE of R&D experience/R&D efficiency: As mentioned above, every innovation in THE DOM operation mode is backed by the front-end’s further pursuit of efficiency and experience. The emergence of virtual DOM provides a highly available vehicle for the idea of data-driven view, enabling front-end development to achieve efficient declarative programming based on functional UI programming.

  2. Cross-platform issues: The virtual DOM is a layer of abstraction from the actual rendered content. Without this layer of abstraction, the view layer is tightly coupled to the rendering platform, and you may have to write two or more completely different sets of code on the Web and Native side to describe the same view content. But now there is a layer of descriptive virtual DOM in the middle, which describes things that can be real DOM, iOS interface, Android interface, small programs…… The same set of virtual DOM can be connected with the rendering logic of different platforms, so as to achieve “once coding, multi-terminal operation”.

Is the implementation of the virtual DOM on the differential update (patch)

In addition to differential updates, batch updates are an important performance effort of the virtual DOM: Batch updates are handled by the Batch function in the common virtual DOM library. In the case of very fast differential updates (such as multiple operations on the SAME DOM in a very short period of time), the user really only sees the effect of the last update. In this scenario, the first few updates don’t make much sense, but they all trigger the re-rendering process, causing a lot of unnecessary and energy-intensive operations

The function of Batch is to buffer the patch set generated each time. It will temporarily store the collected multiple patch sets in the queue, and then give the final result to the rendering function, and finally realize the centralized DOM batch update

The life cycle

componentDidMount

  • The real DOM is mounted and you can perform operations related to the real DOM
  • Asynchronous requests can be performed
  • Data initialization can be performed

getDerivedStateFromProps

  • It is a static method that does not depend on the component instance and cannot access this
  • This method accepts two parameters: props(props from the parent component) and state(state of the current component itself)
  • A return value in object format or NULL is required
  • This approach differs in React 16.3 and 16.4 (and later) in the update process

In React 16.4, getDerivedStateFromProps is triggered by any component update process triggered by any factor (including updates triggered by this.setState and forceUpdate). This lifecycle is triggered only by updates to the parent component.

  • Is it a substitute for componentWillReceiveProps?

Not exactly. This API, relative to the componentWillReceiveProps made reasonable subtraction, from it is defined as a static method can see internal static method can’t get this component instance, This prevents you from doing anything in getDerivedStateFromProps like this.fetch(), irrational this.setState (the kind that causes an infinite loop) that might have side effects

getSnapshotBeforeUpdate

  • The return value of getSnapshotBeforeUpdate is given as the third parameter to componentDidUpdate
  • It is executed after the Render method and before the actual DOM is updated
  • In this phase, we can get the real DOM before and after the update and the state&props information before and after the update
  • For this life cycle, it is necessary to focus on the communication process between it and componentDidUpdate

Why waste componentWillMount, componentWillUpdate, componentWillReceiveProps

Update the React

Before React 16 (described in React 15), every time a component update is triggered, React builds a new virtual DOM tree and diff with the last virtual DOM tree to update the DOM in a directed manner. This is a recursive process. Here is a graphic representation of the characteristics of this process:

As shown in the figure, the recursive call stack for synchronous rendering is very deep, and the entire rendering process starts to return layer by layer only after the lowest call is returned. This long, uninterruptible update process creates a huge risk for the user experience: once synchronous rendering starts, it will hang on to the main thread until the recursion is complete. During this process, the browser can’t handle anything beyond rendering and enters a state where it can’t handle user interaction. So if the rendering time is slightly longer, the page runs the risk of stalling or even getting stuck

React 16 introduced Fiber. Fiber will make the rendering process asynchronous instead of synchronous

  1. Fiber breaks down a large update task into many smaller tasks
  2. Every time a small task is completed, the render thread passes the main thread back to see if there is any higher priority work to be done, thus avoiding the lag caused by synchronous rendering

Here’s the big picture of the life cycle againThe life cycle can be divided into three phases

  1. Render phase: Pure and without side effects, may be suspended, terminated, or restarted by React.
  2. Pre-commit phase: DOM can be read.
  3. Commit phase: You can use DOM, run side effects, and schedule updates

The Render phase allows interruption during execution, whereas the Commit phase is always executed synchronously

Since the render phase is “invisible” to the user, even if the user interrupts and restarts, the user is not aware of it. The commit phase involves rendering the real DOM, changing the view for the user to perceive

Then we understand again, because under the mechanism of Fiber, render phase is allowed to pause, stop and restart, when a task execution after the half to be interrupted, the next rendering thread steal back to the initiative, the task is to restart the form of “repeat again the whole task” rather than “the last one and then perform to the line of code go down”. This leads to the possibility of repeated execution of the render phase lifecycle

harm

With this conclusion in mind, let’s look at the three life cycles, all of which are in the RENDER phase and all of which are likely to be repeated; If they are abused, they can lead to significant risks, such as:

  • setState()
  • Fetch initiates an asynchronous request
  • Manipulating the real DOM

So why is this risky?

  • You can move to other life cycles (especially componentDidxxx)

This is not a harm, but it appears that these methods chicken ribs. A typical example is to make an asynchronous request in componentWillMount, hoping to get data before render to avoid a blank screen. In fact render is so fast that it is impossible to complete an asynchronous request first

  • With Fiber’s asynchronous rendering mechanism, this can lead to serious bugs

Suppose you make a payment request in componentWillUpdate. Since the life cycle in the Render phase can be repeated, multiple payment requests will be issued after componentWillUpdate is interrupted and restarted several times

  • In componentWillReceiveProps and componentWillUpdate setState abuse lead to repeat the render loop

setState

Synchronous or asynchronous?

“SetState is an asynchronous method,” which means that the state itself does not change immediately after we execute setState. So the state output immediately after setState remains in its original state. SetState will execute “just right” at some “magic moment” after the synchronized code has finished executing

What happened to setState?

As can be seen from the figure, a complete update process involves multiple steps including re-render. Re-render itself involves DOM manipulation, which incurs a significant performance overhead. If it is true that one setState triggers a complete update process, then every call to setState triggers a re-render, and our view is likely to get stuck before it is refreshed several times

In the actual React runtime, setState asynchrony is implemented somewhat like a microtask: each setState comes in, it’s shoved into a queue and “saved up.” After the synchronization code is executed, the “saved” state results are combined, and finally only one update process is carried out for the latest state value. This process is called “batch update.”

Why is setState sometimes synchronized?

As can be seen above, the process of setState stack pressing will end at the end of the synchronization task and start reading the stack to perform setState. That is, if your code itself is not synchronous code, there will be no pushdown, and the effect will be synchronous execution. Such as setState in setTimeout

Context

The three elements

  • Create a context object and optionally pass in a defaultValue
Const AppContext = react.createcontext (defaultValue) const {Provider, Consumer} = AppContext We can read Provider and ConsumerCopy the code
  • Use a Provider to wrap the root component in the component tree and pass in a property named “value,” which is the “data” that flows through the component tree and can be consumed by the Consumer
<Provider value={title: this.state.title, content: this.state.content}>
  <Title />
  <Content />
</Provider>
Copy the code
  • Data consumer
<Consumer>
  {value => <div>{value.title}</div>}
</Consumer>
Copy the code

What problem does the new Context API solve?

Context is a new API. Older apis such as getChildContext are deprecated because:

If a Context provided by a component changes and the middle parent’s shouldComponentUpdate returns false, descendants of components using this value will not be updated. Components that use the Context are completely out of control, so there is virtually no way to reliably update the Context

The new API improves on this

Even if the component’s shouldComponentUpdate returns false, it can still propagate through the component to descendant components, ensuring data consistency between data producers and data consumers

Hooks

What is a functional component?

A functional component is a React component that exists as a function. Through inheritance with the corresponding class components React.Com ponent of the React components before there were Hooks, they have the following differences:

Class components need to inherit from class; Class components can access lifecycle methods; Class components can get instantiated this and do all sorts of things based on this, but function components can’t; State can be defined and maintained in a class component; The function component captures the state inside render; Function components are cheaper to learn; .

Why are Hooks needed?

  1. Function components can capture the internal state of render, which is more consistent with the design concept of React framework –UI= F(state), which is more consistent with its design concept and conducive to logical separation and reuse. The Function component is more closely related to the React design concept, but it lacks some “features”. Hooks are used to complement these features
  2. Difficult to understand class refers to two pain points: this itself can be changed, it is uncertain, and it is deceptive. The cost of learning life cycle is too high
  3. Solve the problem that business logic is hard to split. The logic was once coupled with the lifecycle in a class component. It is too big and does too complicated things, which can cause a lot of trouble for readers and maintainers. Best of all, these things seem to have nothing to do with each other. The logic seems to have been “shred” into the lifecycle. With the help of Hooks, we can split these complex operations into different function components according to their logical relationship: We can have a function component for managing subscriptions, a function component for handling the DOM, a function component for getting data, and so on. Hooks help us aggregate business logic and avoid complex components and redundant code

API

useState

Define internal state for functional components; Note that using useState is associated with a state, not a set of states. This is relative to state in the class component. In class components, the state we define is usually an object (a batch of states). In the context of the useState hook, state is a single state, which can be of any JS type

useEffect

Allows function components to perform side effects. UseEffect can now do things that used to be done in componentDidMount, componentDidUpdate, and componentWillUnmount life cycles

useEffect(callBack, [])
Copy the code
  • UseEffect can take two arguments, a callback function and a dependent array.
  • CallBack depends on an array variable and executes when the variable changes.
  • If [] is null, callBack is executed only at mount time;
  • If the callBack needs to return a function, that function is called a “cleanup function.” When React recognizes a cleanup function, it executes the logic inside the cleanup function on uninstall. This rule is not affected by the second argument or other factors, as long as you return a function in the useEffect callback, it will be treated as a cleanup function

Why not call a Hook in a loop, condition, or nested function?

The specific source code is relatively complicated, so this article will not discuss it. Take useState as an example, and generally pay attention to the following points:

  1. Hooks are essentially linked lists
  2. The react-hooks call link is different during the first render and update phases
  3. On first rendering, the actions triggered by useState end up in mountState. The main job of mountState is to initialize Hooks — to converge all the hook information into a hook object. The hook objects are connected in series in the form of one-way linked list. And then finally render
  4. When updating the rendering, go through the previously constructed linked list in order to take out the corresponding data information for rendering
  5. Hooks render by “walking through” each hooks content. If the list is read differently from one another, the result of rendering will be out of control

The stack to reconcile

This is the React 15 concept, and while it may be outdated, it doesn’t hurt to know

What is harmonization

The virtual DOM is a programming concept. In this concept, the UI is stored in memory in an idealized, or “virtual,” representation and synchronized with the “real” DOM through libraries such as ReactDOM, a process called harmonization. Simply put, reconciliation refers to the process of mapping the virtual DOM to the real DOM

What is the Diff

Diff is an important part of the reconciliation process, that is, finding different parts

According to the different implementation forms of Diff, the reconciliation process is divided into “stack reconciliation” represented by React15 and “Fiber reconciliation” since React 16. The following analysis mainly focuses on Diff (React15)

What did Diff do

It compares tree nodes one by one through cyclic recursion. Its algorithm complexity is O(N3). The optimization points of React are as follows:

  1. Hierarchical comparison Combined with the rule that “there are few cross-hierarchical operations between DOM nodes, and same-hierarchical operations are the mainstream”, the React Diff process directly abandons cross-hierarchical node comparison. It only compares nodes of the same hierarchy, and only requires one traversal from top to bottom. I can compare the whole tree what happens if I do cross nodes? React would foolishly assume that the node was no longer needed and would destroy it. It would be expensive to re-create the tree and destroy it. React also advises developers to avoid cross-hierarchy operations and keep DOM structures as stable as possible
  2. In most cases, different types of components have different DOM structures. However, different DOM trees do not need Diff, so React believes that only components of the same type need further comparison. If the two components participating in the Diff are of different types, the comparison is abandoned and the old node is replaced in place
  3. Using Keys to track nodes Keys attempt to address the reuse/swap order of nodes at the same level. It needs to be written inside the element rendered in an array, and it needs to be given a stable and unique value that acts as the element ID. Stability is important here, because React triggers a rerendering of the UI if the key changes. With this key in place, React no longer thinks the nodes need to be rebuilt when the element position is moved — it recognizes the ID and realizes that the nodes have not changed, but have simply been reordered. React can then easily reuse it to “trace” the old node and move it to the new location. As a result, the operation cost of elements at the same level is greatly reduced.

Its disadvantages are as follows:

Because the Stack Reconciler is a synchronous, recursive process, it cannot be interrupted. When dealing with a relatively complex virtual DOM tree, the reconciling /diff time for the Stack Reconciler is lengthy, which means that the JS thread hogs the main thread for a long time. However, JS thread and rendering thread are mutually exclusive, resulting in page rendering stutter/deadlock, interaction for a long time without response and other problems.

Fiber

Due to these performance issues with stack harmonization, React introduced a new harmonization method called Fiber in Version 16

What is the Fiber

From an architectural perspective, Fiber is a rewrite of the React core algorithm (the harmonic process). From the perspective of encoding, Fiber is a data structure defined internally by React. It is the node unit of the Fiber tree structure, which is also the “virtual DOM” under the React 16 architecture. From a workflow point of view, Fiber nodes hold the state and side effects of components that need to be updated, and a Fiber also corresponds to a unit of work

As it is too long, please check another article // Todo

The React event

Native DOM events

  • The DOM event flow

When an event is triggered, it first goes through a trapping process: the event “shuttles” from the outermost element to the innermost element until it reaches the element it is targeting (the element that actually triggered the event). At this point, the event flow switches to the “target phase” — events are received by the target element; The event then “bounces back” into the bubbling phase — it “goes upstream” the way it came, layer by layer, back again

  • To optimize the

Using the bubbling properties of events, the same type of listening logic of multiple child elements is merged into the parent element through a listener function to manage, that is, event delegate. Through event delegation, we can reduce memory overhead, simplify registration steps, and greatly improve development efficiency

How does the React event work?

In React, except for a few special non-bubbling events (such as media-type events) that cannot be handled by the event system, most events are not bound to specific elements. Instead, they are bound to the document of the page. When events are fired on specific DOM nodes, they eventually bubble up to the Document, which is bound to a unified event handler that distributes the event to specific component instances.

React first wraps events before distributing them, wrapping native DOM events as composite events

What are the composite events?

React is a custom event object that complies with the W3C specification. It smooths out differences between browsers at the bottom and exposes developers to a unified, stable event interface that is identical to DOM native events at the top

Although the synthesized event is not a native DOM event, it retains a reference to the native DOM event. When you need access to a native DOM event object, you can get it from the synthesized event object’s E.ativeEvent property

Binding of events

The binding of events is done during the component’s mount, specifically, in the completeWork. There are three key actions within completeWork: Create a DOM node (createInstance), insert the DOM node into the DOM tree (appendAllChildren), and set attributes for the DOM node (finalizeInitialChildren). The set properties section traverses the FiberNode props key. When traversing the functions associated with the event, the registration link for the event is triggered.

  • Event registration link

The event registration process is started by the ensureListeningTo function. In ensureListeningTo, you try to get the root node in the current DOM structure (in this case, the Document object), and then register the unified event-listening function on the Document by calling legacyListenToEvent

  • legacyListenToEvent

LegacyListenToEvent, actually is by calling legacyListenToTopLevelEvent to deal with the relationship between the event and the document In legacyListenToTopLevelEvent logic starting point, Call ListenerMap. has(topLevelType) to determine whether this is true

If listenerMap.has(topLevelType) is true, meaning that the current event document is already listening, then processing of the event will be skipped directly, otherwise the specific event listening logic will be entered. As a result, multiple calls to the same event listener in the React project will trigger only one registration on the Document

ListenerMap is a data structure created/acquired in legacyListenToEvent, which records which events the current Document has been listening for. TopLevelType in legacyListenToTopLevelEvent function within the context of representative types of events, such as the click

  • A registered

Why is the Document listener registered only once for the same event, even though there may be multiple callbacks? React registers the document not with specific callback logic on a DOM node, but with a unified event distribution function

The eventType parameter is the type of event to listen on, and the Listener is the distribution function

  • The distribution function

The listener according to different situation, can be dispatchDiscreteEvent, dispatchUserBlockingUpdate, dispatchEvent.

DispatchDiscreteEvent and dispatchUserBlockingUpdate differences mainly reflected on the processing of the priority, they eventually is by calling the dispatchEvent to perform event distribution. So, the unified event distribution function that you end up binding to document is actually dispatchEvent.

  • dispatchEvent

The essence of event triggering is a call to the dispatchEvent function. The call link triggered by dispatchEvent is long. Here we directly look at the core workflow:

Event trigger, Bubbling to Document -> Execute dispatchEvent -> Create the corresponding composite event object -> Collect the callback functions and corresponding node instances involved in the capture phase of the event -> Collect the callback functions and corresponding node instances involved in the bubbling phase of the event -> The callbacks collected above are executed in sequence, in which the synthesized event object passes in the callbacks

Event triggering

From above, event triggering is actually a call to the dispatchEvent function; In this section we focus on the next steps, collection and callback

Collection process

Let’s look at the source logic

Function traverseTwoPhase(inst, fn, arg) {var path = []; While (inst) {// Collect the current node into the path array path.push(inst); // Collect tag=== Parent of HostComponent inst = getParent(inst); } var i; For (I = path.length; i-- > 0;) { fn(path[i], 'captured', arg); } // From front to back, collect the nodes in the path array that will participate in the bubbling process and the corresponding callback for (I = 0; i < path.length; i++) { fn(path[i], 'bubbled', arg); }}Copy the code

This core function does the following:

  1. The traverseTwoPhase will start at the current node (the target node that triggered the event) and work its way up to the parent of the Tag ===HostComponent. It will collect these nodes in sequence into the PATH array. The tag===HostComponent condition is managed in getParent(). Why do tag===HostComponent have to be required? Because HostComponent is the Fiber node type corresponding to the DOM element. This restriction means that only Fiber nodes corresponding to DOM elements are collected. This is done because browsers only know DOM nodes, browser events are propagated only between DOM nodes, and collecting other nodes is pointless. Note the order of the array path, ascending from the child nodes
  2. Simulate the propagation order of events in the capture phase and collect node instances and callback functions related to the capture phase



    Next, traverseTwoPhase willFrom the back forwardIterate through the PATH array, simulate the capture sequence of events, and collect the callback and instance corresponding to the event in the capture phase.



    As the neutron node of PATH array is in front, the ancestor node traverses the PATH array from back to front, which is actually the process of traversing the child node from the parent node down to the target node, consistent with the propagation sequence in the capture stage. In the traversal process, fn function will check the callback of each node. If the capture callback corresponding to the current event on this node is not null, The node instance is then collected in the _dispatchInstances property of the SyntheticEvent, Event callbacks are collected on the _dispatchListeners property of synthetic events (syntheticEvent._dispatchListeners) for subsequent execution.
  3. Simulate the propagation order of events in the bubbling phase, collect the node instances and callback functions related to the bubbling phase



    After the capture phase of the work is complete, the traverseTwoPhase willFrom the back forwardIterate through the PATH array, simulate the bubbling order of events, and collect the corresponding callbacks and instances of events during the capture phase.



    Since backward traversal simulates the event propagation order in the capture stage, positive traversal naturally simulates the event propagation order in the bubbling stage. In the process of sequential traversal, the callback of each node is also checked. If the bubble callback corresponding to the current event on this node is not empty, Instances of nodes and event call-ins are also collected by SyntheticEvent._dispatchinInstances and SyntheticEvent._dispatchListeners respectively.



    参 数 SyntheticEvent 参 数 SyntheticEvent 参 数 SyntheticEvent 参 数 SyntheticEvent 参 数 SyntheticEvent Collected event callbacks are also pushed into the same SyntheticEvent._dispatchlisteners.



    This allows the entire DOM event stream to be simulated in one sitting by executing the syntheticEvent._dispatchListeners array in sequence during the event callback phase, which is the catch-target-bubble phase

reference

React-hooks work