Vinícius Vidal

Full-Stack Developer

O que é um re-render no React

Todo re-render é causado por uma mudança de estado.

image.png

Por que um componente renderiza novamente?

De forma grosseira, o principal papel do React é garantir que a interface esteja sempre atualizada de acordo com o estado da aplicação. Quando algo no estado muda, o React faz um re-render para descobrir exatamente o que precisa ser alterado na tela. É assim que ele mantém tudo em sincronia.

Podemos dividir esse processo em 3 etapas:

  1. Triggering: Algo muda (estado, props).
  2. Rendering: React recalcula a UI.
  3. Committing: Aplica as mudanças na DOM.

Agora na prática: vamos imaginar um contador e um botão para incrementar. Quando ocorre o primeiro render (chamado de initial render) esse contador inicia em 0. Agora, quando o usuário clicar no botão, o estado vai mudar de 0 para 1. Como dito anteriormente, devido à mudança de estado, é disparado um re-render: etapa de triggering. o React então executa o código novamente utilizando o novo valor do estado: etapa de rendering.

Cada renderização é uma snapshot, como se fosse uma foto, que é um esqueleto de como a interface deve ser estruturada de acordo com o estado atual da aplicação.

Com isso, temos duas snapshots até agora: uma com o contador em 0 e outra com ele em 1.

O React então compara essas duas snapshots e verifica o que mudou entre as duas, assim então atualizando na DOM: etapa de committing. O React somente atualiza a DOM (interface) se houver diferença entre as renderizações.

image.png

Na imagem acima, os componentes em verde são os que sofrem um re-render. Podemos perceber que, quando o componente “Counter” tem uma mudança de estado, tanto ele quanto todos os seus filhos são re-renderizados. Neste exemplo há apenas um filho direto, mas isso se aplica a toda a árvore de componentes descendente, não importa a profundidade: se um componente pai re-renderiza, todos os seus filhos também serão re-renderizados por padrão.

Afinal, props causam re-render?

Você provavelmente já ouviu que mudanças nas props disparam um re-render, mas o ponto é, quando um componente renderiza novamente, todos os seus descendentes também passam por isso.

Vamos imaginar a seguinte estrutura:

image.png

Os componentes em verde são os que sofreram um re-render após a mudança do estado count. Mas... por que o componente Description também re-renderizou? 🤯

A resposta é simples: quando um componente re-renderiza, todos os seus filhos também passam pelo processo de re-render.

Pode até passar pela sua cabeça: “mas o componente Description nem recebe a prop count”. Uma dúvida justa. Mas o que acontece é que o React faz um re-render recursivo em todos os componentes descendentes, independentemente de eles receberem props diretamente ou não. Vale lembrar: re-renderizar não significa necessariamente atualizar a DOM. O React ainda vai comparar os snapshots (com o Virtual DOM) para decidir o que realmente precisa ser alterado na tela.

Componentes puros

Uma função pura é aquela que, dado o mesmo input, sempre produz o mesmo output. O resultado dela depende apenas dos valores que recebe como entrada.

O mesmo vale para componentes puros, são aqueles que temos a garantia de que o resultado sempre será o mesmo.

Segue como fazer isso:

function HeaderComponent() {
  return (
    <h1>My Blog</h1>
  );
}

export const Header = React.memo(HeaderComponent);

Dessa forma, estamos basicamente dizendo ao React: “pode confiar no pai”. Com isso, o React deixa de re-renderizar aquele componente específico sempre que o pai re-renderiza. Componentes puros (como os criados com React.memo, por exemplo) só vão re-renderizar se suas props realmente mudarem, e é aí que conseguimos evitar re-renders desnecessários. Essa técnica é chamada de memoization.

Você pode estar imaginando o porquê dessa não ser o comportamento padrão do React, porém, é necessário entender que re-renders não são tão caros, e usar a técnica de memoization sem pensar duas vezes pode ser ainda pior por ter que ficar checando se as props mudaram.

Segue a prova disso:

image.png

Por mais complexo que esse código pareça, vou simplificar: a função shallowEqual basicamente usa o Object.is para comparar as props antigas com as novas. Se todas forem iguais, o React dá um bailout, ou seja, ele "desiste" de fazer o re-render daquele componente, porque entende que nada mudou.

E se fosse correto aplicar essa técnica em todos os components, seria o padrão do React.

Como ficam os contextos

O comportamento não muda muito quando se trata de contextos no React. Todos os filhos de um contexto ainda irão sofrer um re-render com a mudança de estado.

Quando um componente puro consome um contexto, o React consegue detectar isso e disparar re-renders com a mudança de estado do contexto, como se fossem “props invisíveis”. Exemplo:

function SignedComponent() {
  const { user } = React.useContext(AuthContext);

	 if (!user) {
		 return 'Entrar';
	 }

  return 'Bem vindo!!!'
}

export const Signed = React.memo(SignedComponent);

Nesse exemplo user é uma dependência do componente Signed e toda vez que mudar irá disparar um re-render.

E é isso!

Espero que tenham entendido o que é um render no React.

Até a próxima, dev! 👋