Re-renderizações desnecessárias de TextField

Nov 06 2020

Percebi um problema em que todos os componentes do mesmo pai ( App no exemplo abaixo) serão renderizados novamente quando os estados / props não relacionados mudarem, tornando a página / formulários perceptíveis mais lentos.

Eu segui muitos conselhos, como memorizar manipuladores de eventos e adereços, mas componentes não relacionados ainda são renderizados novamente. Estou perplexo. O que não estou entendendo sobre React?

[ CodeSandbox ] No depurador React, habilitar: Destacar atualizações quando os componentes forem renderizados

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>
    </>
  );
}

Respostas

1 Jayce444 Nov 06 2020 at 06:52

As ferramentas do depurador são bugadas em relação aos componentes memoized. Quando você memoriza os componentes, eles não são realmente renderizados, mas as ferramentas de depuração os destacam de qualquer maneira (vejahttps://github.com/facebook/react/issues/19778)

Para realmente testar a re-renderização, altere o valor de estado padrão para a segunda entrada (que é renderizada muitas vezes) para algo como "teste", em seguida, coloque um console.logdentro de um MyTextInputcomponente memoizado para ver quais estão realmente sendo renderizados novamente:

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

Você pode ver que ele imprime o valor de "teste" uma vez para todas as segundas entradas quando elas são renderizadas pela primeira vez, então, conforme você digita a primeira, não está registrando todas as segundas entradas console.loggraças à memoização.