skip to Main Content

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


  1. 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.

    Login or Signup to reply.
  2. Are there two senses of "re-rendering"?

    "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 the ExpensiveTree 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"
    enter image description here

    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

    1. state changed
    2. context changed
    3. hook changed ( ex: useContext() )
    4. parent re rendered

    You can avoid it getting re render using a memo, (to avoid re renders due to parent re renders)

    const ExpensiveTree = React.memo(() => {})
    

    or you can use composition.

    const App = ({ children }) => {
     return <div>
      <input value={color} onChange={(e) => setColor(e.target.value)} />
      <p style={{ color }}>Hello, world!</p>
      {children}
     </div>
    }
    
    return <App>
     <ExpensiveTree />
    </App>
    

    Now ExpensiveTree is not going to re render because its not a child but a prop of App component.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search