Executando verificação de segurança...
7

Entendendo os re-renders e a memorização no React

Quando o React re-renderiza novamente os componentes?

Geralmente, uma nova renderização é causada por props de um componente ou mudança de estado. Quando um componente renderiza novamente, todos os seus componentes filhos também serão renderizados novamente, a menos que sejam memorizados. (Há exceções, mas isso é principalmente verdade.)

useRef

https://reactjs.org/docs/hooks-reference.html#useref

Um erro comum que os desenvolvedores do React cometem é utilizar o useState para cada valor mutável que eles precisam para persistir entre as renderizações (inclusive, antes eu aprender corretamente os hooks, eu mesmo fazia isso).

useState é uma boa solução se a saída renderizada depender do valor, caso contrário useRef seria uma solução mais ideal.

Considere o seguinte exemplo:

const [firstName, setFirstName] = useState();

return (
  <form onSubmit={() => alert(firstName)}>
    <input onChange={(e) => { setFirstName(e.target value) }} />
    <button>Submit</button>
  </form>
);

Neste exemplo, toda vez que o usuário digita, o estado firstName é atualizado. Quando um estado é atualizado, uma nova renderização é acionada, o que significa que uma nova renderização está acontecendo toda vez que o usuário digita.

Como firstName não está sendo usado na saída renderizada, podemos substituí-lo por useRef e evitar a nova renderização, resultando neste codigo:

const firstName = useRef();

return (
  <form onSubmit={() => alert(firstName.current)}>
    <input
      onChange={e => {
        firstName.current = e.target.value;
      }}
    />
    <button>Submit</button>
  </form>
);

memo

https://reactjs.org/docs/react-api.html#reactmemo

Um dos conceitos mais importantes a serem entendidos para otimizar o React é a memoização.

Sobre memoização

A memoização é o processo de armazenar em cache os resultados de uma função e retornar o cache para solicitações subsequentes.

Algoritmo de Reconciliação

Re-renderizar um componente significa simplesmente chamar a função do componente novamente. Se esse componente tiver componentes filhos, ele chamará as funções desses componentes e assim por diante até a árvore. Os resultados são então diferidos com o DOM para determinar se a interface do usuário deve ser atualizada. Esse processo de diferenciação é chamado de reconciliação.

Como os componentes são apenas funções, eles podem ser memorizados usando React.memo(). Isso evita que o componente seja renderizado novamente, a menos que as dependências (props) tenham sido alteradas. Se você tiver um componente particularmente pesado, é melhor memorizá-lo, mas não memoize todos os componentes.
A memoização usa memória e pode ter menos desempenho em certos casos.

Como funciona o Algoritmo de Reconciliação?

Quando um componente é memorizado, em vez de renderizá-lo novamente, o React diferencia as novas props do componente com suas props anteriores. A desvantagem que precisa ser considerada aqui é o quão intensivo é comparar os adereços com a execução da função. Se você tiver um objeto grande em seus adereços, pode ser menos eficiente memorizar esse componente.

Exemplo:

const HeavyComponent: FC = () => {
  return <div />;
};

export const Heavy = React.memo(HeavyComponent);

useCallback

https://reactjs.org/docs/hooks-reference.html#usecallback

Uma ferramenta importante para evitar que componentes memorizados sejam renderizados desnecessariamente é o useCallback.

Ao passar uma função para um componente memoizado, você pode remover inadvertidamente o efeito de memorização, não memorizando essa função usando useCallback.

A razão para isso é a igualdade referencial. Como mencionado anteriormente, cada re-renderização chama a função de um componente. Isso significa que se estivermos declarando uma função no componente, uma nova função será criada a cada nova renderização.

Se estivermos passando essa função como um prop para outro componente, mesmo que o conteúdo da função não mude, a referência muda, o que faz com que o componente filho seja renderizado novamente, mesmo que seja memorizado.

Exemplo:

export const ParentComponent = () => {
  const handleSomething = () => {};

  return <HeavyComponent onSomething={handleSomething} />;
};

Neste exemplo, toda vez que o ParentComponent for renderizado novamente, o HeavyComponent também será renderizado novamente, mesmo que seja memorizado. Podemos corrigir isso usando useCallback e impedir que a referência seja alterada.

export const ParentComponent = () => {
  const handleSomething = useCallback(() => {}, []);

  return <HeavyComponent onSomething={handleSomething} />;
};

Em breve falarei quando usar o useCallback e quando não utilizar.

useMemo

https://reactjs.org/docs/hooks-reference.html#usememo

Até agora sabemos que cada re-renderização significa que a função do componente está sendo chamada novamente. Isso significa que se sua Função de Componente inclui uma chamada para uma função pesada — essa função pesada está sendo chamada a cada nova renderização.

Para evitar executar a função pesada a cada nova renderização, você pode memorizá-la. A primeira renderização chamará a função e as seguintes renderizações retornarão os resultados armazenados em cache da função, em vez de executá-la novamente.

Veja:

const value = useMemo(() => expensiveFunction(aDep), [aDep]);

Em nosso exemplo, o valor será armazenado em cache e atualizado apenas quando aDep for alterada.

Conclusão

Esta publicação foi escrita por Kolby Sisk, e traduzida por Matheus Filype.

Se você quer se aprofundar mais sobre renderizações, confira o guia completo para renderizações por Mark Erikson's

Carregando publicação patrocinada...
2
2

Incrível, Filipe! Estou muito feliz em saber disso.. Isso me motivou a continuar escrevendo cada vez mais. Obrigado pelo aviso.

1
1
1