Vinícius Vidal
Full-Stack Developer
O que é um re-render no React
Todo re-render é causado por uma mudança de estado.
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:
- Triggering: Algo muda (estado, props).
- Rendering: React recalcula a UI.
- 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.
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:
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:
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! 👋