9

Please refer to this URL in the React DOCS. A version of this code is also available here.

I understand that inside a Functional React Component, it is preferred to use the useCallback hook to create a ref callback as shown in the React Docs URL above, but I wanted to understand what would happen if, instead, a simple arrow function ( inline function ) is used as a ref callback.

So, below, I have modified the code from the above URL to not use the useCallback hook. Instead, I am just using a regular arrow function as a ref callback. Additionally, I have added two console.log statements. Here is the code which is also available at this URL.

import React, { useState } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [height, setHeight] = useState(0);

  const measuredRef = node => {
    console.log("Setting height. node = ", node);
    if (node !== null) {
      setHeight(node.getBoundingClientRect().height);
    }
  };

  console.log("Rendering.");
  return (
    <div className="App">
      <h1 ref={measuredRef}>Hello, world</h1>
      <h2>The above header is {Math.round(height)}px tall</h2>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

On loading this app, the following are printed (with numbering added):

  1. Rendering.
  2. Setting height. node =  <h1>Hello, world</h1> 
  3. Rendering.
  4. Setting height. node =  null
  5. Setting height. node =  <h1>Hello, world</h1>
  6. Rendering.

Why is the ref callback called three times and why does the component render three times on initial load?

Alan C. S.
  • 14,718
  • 2
  • 20
  • 16

2 Answers2

9

Why is the ref callback called three times and why does the component render three times on initial load?

Mainly because inside your callback ref, measuredRef(), you're doing a state update via setHeight().

Here's a step-by-step explanation:

  1. Rendering: Initial Render
  2. Setting height node = <h1>Hello, world</h1>: Initial Render, Ref assignment
  3. Rendering.: Component Re-render due to set height

For the last two prints, refer to caveats of callback refs:

If the ref callback is defined as an inline function, it will get called twice during updates, first with null and then again with the DOM element.

  1. Setting height. node = null: null due to height update
  2. Setting height. node = <h1>Hello, world</h1>: now with DOM element

UPDATE (for the last render #6):

  1. The last render was due to #5 where node != null. So setHeight was called.

i.e #4 (node = null) doesn't cause a re-render cause height is only set if node != null.

Joseph D.
  • 11,804
  • 3
  • 34
  • 67
1

See the caveat with ref callbacks: https://reactjs.org/docs/refs-and-the-dom.html#caveats-with-callback-refs

Without useCallback, a new function callback function measuredRef is created on every render.

ckedar
  • 1,859
  • 4
  • 7