TextField Unnecessary Re-renders

Viewed 1513

I've noticed an issue where every component within the same parent (App in below example) will rerender when unrelated states/props change, making the page/forms noticeable slower.

I've followed many advice like memoizing event handlers & props, but unrelated components still re-renders. I'm stumped. What am I not understanding about React?

[CodeSandbox] In React debugger, enable: Highlight updates when components render

import React, { useMemo, useState } from "react";
import { TextField } from "@material-ui/core";

function MyTextInput(props) {
  return (
    <TextField
      variant={"outlined"}
      onChange={props.onChange}
      value={props.value}
    />
  );
}

export default function App() {
  const [exampleTextValue1, setExampleTextValue1] = useState("");
  const [exampleTextValue2, setExampleTextValue2] = useState("");

  const handleChange1 = useMemo(
    () => (event) => setExampleTextValue1(event.target.value),
    []
  );

  const handleChange2 = useMemo(
    () => (event) => setExampleTextValue2(event.target.value),
    []
  );

  return (
    <>
      <div>
        Change me:
        <MyTextInput value={exampleTextValue1} onChange={handleChange1} />
      </div>

      <div>
        Unrelated inputs. Should not re-render:
        <MyTextInput value={exampleTextValue2} onChange={handleChange2} />
        <MyTextInput value={exampleTextValue2} onChange={handleChange2} />
        {/*  to feel the impact, copy the above line like 98 more times  */}
      </div>
    </>
  );
}

1 Answers

The debugger tools are bugged in regards to memoized components. When you memoize components they don't actually re-render, but the debugging tools highlights them anyway (see https://github.com/facebook/react/issues/19778).

To actually test the re-rendering, change the default state value for the second input (which is rendered lots of times) to something like "test", then put a console.log inside a memoized MyTextInput component to see which are actually getting re-rendered:

const MyTextInput = React.memo((props) => {
  console.log(props.value);
  return (
    <TextField
      variant={"outlined"}
      onChange={props.onChange}
      value={props.value}
      disabled={props.disabled}
    />
  );
});

You can see it prints the "test" value once for all the second inputs when they first render, then as you type in the first, it isn't logging all the second inputs console.logs thanks to the memoization.

Related