I thought the following statement is true in React:
In React, when there is no props change and no state change, there is no re-rendering
However, when I read the blog post:
https://overreacted.io/before-you-memo/
and the code is:
import { useState } from 'react';
export default function App() {
let [color, setColor] = useState('red');
return (
<div>
<input value={color} onChange={(e) => setColor(e.target.value)} />
<p style={{ color }}>Hello, world!</p>
<ExpensiveTree />
</div>
);
}
function ExpensiveTree() {
let now = performance.now();
while (performance.now() - now < 100) {
// Artificial delay -- do nothing for 100ms
}
return <p>I am a very slow component tree.</p>;
}
You can try it here. I intentionally make the delay even bigger, so that you probably will see your typing is so slowly reflected in the UI that it is unbearable: https://codesandbox.io/s/focused-cache-vvv426?file=/src/App.js
So my question is: that line
<ExpensiveTree />
has no prop changes and no state changes involved (as far as ExpensiveTree
is concerned), so there should be no re-rendering. But why is it being re-rendered?
Are there two senses of "re-rendering"? One is diff’ing with Virtual DOM and actually updating something, and the other is does this function component actually run? Somehow I have this wishy-washy understanding about this.
2
Answers
The delay in your code is an artifact of javascript, not React. In it’s simplest form React works by serving an index.html file and attaching code to a node in the DOM tree. If you wait synchronously then React is prevented from the initial load, resulting in just the blank index file being visible until the first render. So what you’re seeing is not a re-render, just a delay of the first render.
Your example is a good showcase of why React uses hooks. The point is to show something on the page as fast as possible and then re-render as cheaply as possible. Hooks accomplish this by all being synchronous (in the component at least) and then updating state, but the details of course vary.
"Rendering" is the process of building the new virtual dom and identifying which parts need to change using reconciliation algorithm. calling your components are just a part of it.
In your case,
ExpensiveTree
goes through this rendering process, but once the diffing completed with reconciliation algorithm it has nothing to commit to the actual DOM. So in "Commit" phase no changes will made to the actual DOM related to theExpensiveTree
component.So to see why it re render, run the profiler and check the reason for re render, you can see "The parent component rendered"
That’s how React works, it doesn’t matter whether prop change or not, if parent get re renders, its children are also get called.
A component can get re render, if
You can avoid it getting re render using a memo, (to avoid re renders due to parent re renders)
or you can use composition.
Now
ExpensiveTree
is not going to re render because its not a child but a prop ofApp
component.