最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why does React discard the entire DOM subtree and recreate it from scratch? - Stack Overflow

programmeradmin0浏览0评论

There are two helpers that can be used to add content while rendering:

  ...

  const DisplayA = () => <div className={'containerA'}>
    <button onClick={handleToggleA}>{"A toggled: " + toggledA.toString()}</button>
  </div>

  const displayB = () => <div className={'containerB'}>
    <button onClick={handleToggleB}>{"B toggled: " + toggledB.toString()}</button>
  </div>

  return (
    <>
      <DisplayA />
      { displayB() }
    </>
  );

  ...

The problem is that in the first helper, React always discards the entire subtree and creates it again from scratch, as can be seen here:

The demo

I know, the first way is syntax sugar for React.createElement so a new ponent is created each render. However, the second way, a distinct arrow function is created each render too.

  1. Why does React not know how to reuse the subtree the first way but does know the second way? What is happening under the hood?

  2. How can we spot when the DOM subtree is discarded and recreated each render? Is it enough to assume that one should not create inline ponents, and use inline functions only?

Notice, that helpers can e from props, for example (render props pattern).

There are two helpers that can be used to add content while rendering:

  ...

  const DisplayA = () => <div className={'containerA'}>
    <button onClick={handleToggleA}>{"A toggled: " + toggledA.toString()}</button>
  </div>

  const displayB = () => <div className={'containerB'}>
    <button onClick={handleToggleB}>{"B toggled: " + toggledB.toString()}</button>
  </div>

  return (
    <>
      <DisplayA />
      { displayB() }
    </>
  );

  ...

The problem is that in the first helper, React always discards the entire subtree and creates it again from scratch, as can be seen here:

The demo

I know, the first way is syntax sugar for React.createElement so a new ponent is created each render. However, the second way, a distinct arrow function is created each render too.

  1. Why does React not know how to reuse the subtree the first way but does know the second way? What is happening under the hood?

  2. How can we spot when the DOM subtree is discarded and recreated each render? Is it enough to assume that one should not create inline ponents, and use inline functions only?

Notice, that helpers can e from props, for example (render props pattern).

Share Improve this question edited Oct 22, 2019 at 17:25 Boann 50.1k16 gold badges124 silver badges152 bronze badges asked Oct 22, 2019 at 15:29 DanielDaniel 1,6115 gold badges20 silver badges41 bronze badges 1
  • There's a couple of answers to your question now, are either suitable, or do you need something else? – ggovan Commented Oct 24, 2019 at 15:42
Add a ment  | 

2 Answers 2

Reset to default 5

This will depend in which scope DisplayA is defined. Functional ponents should usually be defined at the top level of a file. In your demo DisplayA is a ponent that is created inside the render of App, so every time App renders a new functional ponent is created, not a new invocation of the same ponent.

To resolve this make DisplayA top level in the file and pass props through to it.

const DisplayA = ({handleToggle, toggled}) => <div className={'containerA'}>
  <button onClick={handleToggle}>{"A toggled: " + toggled.toString()} </button>
</div>

const App = () => {
  ...
  return <>
    <DisplayA handleToggle={() => {...}} toggle={...} />
  </>
}

The second approach does not create a ponent which is passed to react for reconciliation, but is a function which is invoked while rendering and puts the contained elements into the rendering of this ponent.

In the first case you are not calling DisplayA. Instead, you are letting react decide when to render it. Notice how when transpiled, React.createElement(DisplayA) does not invoke this function. When react renders the subtree, it decides what needs to be re-rendered.

The process of handling tree changes/updates is called reconciliation. In react docs it says that same types will try to maintain state, whereas different ponent types will always perform a tear down on the DOM tree.

The latter happens with your DisplayA ponent because it's a different value on every render. Although the ponent renders the same view, React can't be sure that this is the same ponent because the value DisplayA points to a different ponent reference each time. In this case you are using function ponents, therefore the value is a reference to a new function. The variable name just happens to be the same - it does not have any importance at run time.

In the second case with displayB you are explicitly calling the function and rendering its result. Because the function is pure, this is equivalent to pulling its return value and inlining it inside the parent ponent:

return (
  <>
    <DisplayA />
    { 
      <div className={'containerB'}>
        <button onClick={handleToggleB}>{"B toggled: " + toggledB.toString()}</button>
      </div>
    }
  </>
)

Notice how the second child of this fragment is now a div. Because divs are primitive elements, they are represented by the literal string 'div' and not a reference to a ponent. React knows between renders that this is the same tree and as such does not destroy it. This would also work if you had any external ponent with a stable reference - it will be considered an element of the same type.

发布评论

评论列表(0)

  1. 暂无评论