TL; DR

This paper includes:

  1. Two examples of using “immutable virtual DOM” to avoid component Render
  2. What is an “immutable virtual DOM”
  3. React reconcile phase pseudocode
  4. The “immutable virtual DOM” avoids component Render

Patients with a

Does the Child component rerender after the Parent component updates its state every second? Please click here for online Demo.

function Child() {
  return <div>Child components</div>
}

// This Hook is used to update the status every second
function useUpdateStateEverySecond() {
  const [, setState] = useState(0)
  useEffect(() = > {
    const timer = window.setInterval(() = > {
      setState(v= > v + 1)},1000)
    return () = > clearInterval(timer)
  }, [setState])
}

function Parent({ children: child }) {
  useUpdateStateEverySecond()

  return (
    <div>The parent component child} {</div>)}function App() {
  return (
    <Parent>
      <Child />
    </Parent>)}Copy the code

At first glance it is obvious that the Child component will be rerendered. But in reality: while the Parent component renders again after updating the state every second, the Child component does not.

This example optimization technique corresponds to the React performance optimization of lowering the state to reduce the scope of state influence.

Example 2

The code is as follows, using useMemo to implement the “immutable virtual DOM”. Please click here for online Demo.

function Child() {
  return <div>Child components</div>
}

function Parent() {
  useUpdateStateEverySecond()

  const child = useMemo(() = > <Child />[]),return (
    <div>The parent component child} {</div>)}Copy the code

This example optimization technique corresponds to the React performance optimization “useMemo returns the virtual DOM”, which is a commonly used performance optimization method.

Example 3 (before optimization)

For comparison with the previous two examples, the component writing before optimization is also listed here, with the following code. Please click here for online Demo.

function Child() {
  return <div>Child components</div>
}

function Parent() {
  useUpdateStateEverySecond()

  return (
    <div>The parent component<Child />
    </div>)}Copy the code

In this example, both the Parent and Child components are rendered again every second the state is updated.


Although example 1 and Example 2 have different code organization and optimization ideas, the underlying principles are the same for React.

To explain how this works, we need to start with JSX and the virtual DOM to understand what “immutable virtual DOM” is. Combined with the pseudo-code of React harmonization stage, the principle behind optimization is clearly described.

Immutable virtual DOM

JSX is a syntax for describing the virtual DOM. The JSX code we wrote
will eventually be converted to react. createElement(Child, {}, null). See React Without JSX to learn more about the JSX code.

In examples 1 and 2, the return value of the Parent function can be expressed as createElement:

function Parent() {
  return createElement(div, {}, ["Parent component", child])
}
Copy the code

In example 3, the Parent function returns:

function Parent() {
  return createElement(div, {}, ["Parent component", createElement(Child, {}, null)])}Copy the code

The difference between the two codes is stark. Before optimization, when the Parent function is executed, the virtual DOM corresponding to the Child component is regenerated. The virtual DOM corresponding to the Child component does not change after optimization; it is always Child.

React Reconciliation Phase (Simplified version)

To illustrate the principle behind optimization, the React reconciliation phase is simplified into the following pseudocode:

/** * next indicates the virtual DOM after the status update */
function reconcile(current, next) {
  let needRender = false
  if(current.props ! == next.props) {// If the Props of the virtual DOM are new, the Render process needs to be executed
    needRender = true
  } else if (current.stateHasChanged) {
    // The state of the virtual DOM has been updated and the Render process needs to be executed
    needRender = true
  } else if (current.descendantStateHasChanged) {
    // The descendant of the virtual DOM has a status update and will not execute the Render process of the component
    // However, the conciliation phase will be performed recursively on the descendant virtual DOM
    reconcile(current.child, current.child)
  }

  if (needRender) {
    Func represents the component functions corresponding to the virtual DOM, such as Parent and Child in the example
    nextChild = current.func(current.props)
    reconcile(current.child, nextChild)
  }
}

// The starting point of the reconciliation phase is:
reconcile(currentRoot, currentRoot)
Copy the code

The React code is available here.

follow

In example 3, after updating the state of the Parent component, the virtual DOM corresponding to the Child component is newly generated. Therefore, current. Props will not equal next. Props, so the Child component will be re-render.

In example 2, after updating the state of the Parent component, the Parent component rerenders. The virtual DOM corresponding to the child component remains unchanged because the child component is cached using useMemo, and current-props is equal to next-props. Since the virtual DOM has no status updates, there is no need to rerender.

In example 1, after updating the state of the Parent component, the Parent component is re-render. But because the App component does not rerender, the children property received by the Parent component remains the same. Therefore, for the virtual DOM corresponding to the Child component, current-props is equal to next-props. Since the virtual DOM has no status updates, there is no need to rerender.

conclusion

Although these three examples correspond to the same JSX code, they are all:

<Parent>
  <Child />
</Parent>
Copy the code

But in example 3 (before optimization), the virtual DOM corresponding to
is regenerated for each status update. In the optimized examples 1 and 2, the virtual DOM corresponding to
remains unchanged.

During the reconciliation phase, if the virtual DOM is unchanged (that is, the props references are equal) and there are no state updates, then the reconciliation phase of the virtual DOM is skipped.

This is why performance tuning can be done using the immutable virtual DOM.

Recommend more React articles

  1. The React performance optimization | including principle, technique, Demo, tools to use
  2. Talk about useSWR to improve your development – including useSWR design ideas, pros and cons, and best practices
  3. Why does React use the Lane solution
  4. Why does React Scheduler use MessageChannel

, recruiting

The author works in Chengdu – Bytedance – private cloud, and the main technology stack is React + Node.js. Team expansion speed is fast, the technical atmosphere within the group is active. Public cloud Private cloud has just started, and there are many technical challenges.

Interested can resume through this link: job.toutiao.com/s/e69g1rQ

You can also add my wechat “moonball_CXY” to chat and make friends.

Original is not easy, don’t forget to praise oh ❤️