Guia de React re-render: tudo, tudo de uma vez
Tags: react, javascript, portugues
Published: 2024-05-31 19:25:57

> _**NOTA:** Este artigo é apenas uma tradução. A fonte dele está no rodapé._

Guia completo sobre re-renders do React. O guia explica o que são re-renders, o que é um re-render necessário e desnecessário, o que pode acionar um re-render de React component.

Também inclui os patterns mais importantes que podem ajudar a evitar re-renders e alguns anti-patterns que levam a re-renders desnecessários e, como resultado, baixo desempenho. Cada pattern e anti-pattern é acompanhado por um auxílio visual e um exemplo de código funcional.

![cat reading](https://www.developerway.com/_next/image?url=%2Fassets%2Freact-re-renders-guide%2Fwelcome.png&w=1080&q=75)

## O que é o re-render no React?

Ao falar sobre o desempenho do React, há dois estágios principais com os quais precisamos nos preocupar:

-   **initial render**: acontece quando um component aparece pela primeira vez na tela
-   **re-render**: segunda e qualquer renderização consecutiva de um componente que já está na tela

Re-render acontece quando o React precisa atualizar o app com alguns dados novos. Geralmente, isso acontece como resultado da interação de um usuário com o app ou de alguns dados externos provenientes de uma request assíncrona ou de algum modelo de subscription.

Apps não interativos que não possuem atualizações de dados assíncronas nunca serão re-render e, portanto, não precisam se preocupar com a otimização do desempenho das novas renderizações.

[Assista "Intro to re-renders" no YouTube](https://youtu.be/qTDnwmMF5q8)

### 🧐 O que é um re-render necessário e o que é um re-render desnecessário?

**Re-render necessário**: re-render de um component que é a origem das alterações ou de um component que usa diretamente as novas informações. Por exemplo, se um usuário digitar um input field, o component que gerencia seu estado precisa se atualizar a cada pressionamento de tecla, ou seja, re-renderizar.

**Re-render desnecessário**: re-render de um component que é propagado pelo app por meio de diferentes mecanismos de re-render devido a erro ou arquitetura ineficiente do app. Por exemplo, se um usuário digitar um input field e a página inteira for re-render a cada pressionamento de tecla, a página terá sido re-render desnecessariamente.

Re-renders desnecessários por si só não são um problema: React é muito rápido e geralmente capaz de lidar com elas sem que os usuários percebam nada.

No entanto, se os re-renders acontecerem com muita frequência e/ou em components muito pesados, isso poderá fazer com que a experiência do usuário pareça com "lag", delays
visíveis em cada interação ou até mesmo o app pare completamente de responder.

[Assista "Intro to re-renders" no YouTube](https://youtu.be/qTDnwmMF5q8)

## Quando um React component re-render novamente?

Existem quatro razões pelas quais um component re-render: mudanças de state, re-renders de parents (ou childrens), mudanças de context e mudanças de hooks. Há também um grande mito: que as re-renders acontecem quando os props do component mudam. Por si só, não é verdade (veja a explicação abaixo).

### 🧐 Razões para re-renders: mudanças de state

Quando um state do component muda, ele irá re-render novamente. Geralmente, isso acontece ou em um callback ou no `useEffect` hook.

As mudanças de state são a fonte "raiz" de todos os re-renders.

-   [Veja um exemplo no codesandbox](https://codesandbox.io/s/part2-1-re-renders-because-of-state-ngh8uc?file=/src/App.tsx)
-   [Assista "Intro to re-renders" no YouTube](https://youtu.be/qTDnwmMF5q8)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part2-state-changes-example.png)

### 🧐 Razões para re-renders: re-render de parents

Um component será re-render se seu parent também re-render. Ou, se olharmos para isso na direção oposta: quando um component é re-render, ele também re-render todos os seus children.

Ele sempre "desce" na árvore: o re-render de um child não aciona o re-render de um parent. (Existem algumas advertências e casos extremos aqui, consulte o guia completo para mais detalhes: [O mistério do React Element, children, parents e re-renders](https://www.developerway.com/posts/react-elements-children-parents)).

-   [Veja um exemplo no codesandbox](https://codesandbox.io/s/part-2-2-re-renders-because-of-parent-b0xvxt?file=/src/App.tsx)
-   [Assista "Intro to re-renders" no YouTube](https://youtu.be/qTDnwmMF5q8)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part2-parent-example.png)

### 🧐 Razões para re-renders: mudanças de context

Quando o valor no Context Provider for alterado, todos os components que usam esse Context serão re-render, mesmo que não usem a parte alterada dos dados diretamente. Esses re-renders não podem ser evitados diretamente com a memoization, mas existem algumas workarounds que podem simular isso.

- [Veja um exemplo no codesandbox](https://codesandbox.io/s/part-2-3-re-render-because-of-context-i75lwh?file=/src/App.tsx)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part2-context-example.png)

### 🧐 Razões para re-renders: mudanças nos hooks

Tudo o que está acontecendo dentro de um hook "pertence" ao component que o utiliza. As mesmas regras relativas às mudanças de context e state se aplicam aqui:

-   a mudança de state dentro do hook irá desencadear uma re-rerender inevitável do component "host"
-   se o hook usar o Context e as alterações de valor do Context, ele acionará uma re-rerender inevitável do component "host"

Hooks podem ser encadeados. Cada hook único dentro da cadeia ainda "pertence" ao "host" component, e as mesmas regras se aplicam a qualquer um deles.

-   [Veja um exemplo no codesandbox](https://codesandbox.io/s/part-2-4-re-render-because-of-hooks-5kpdrp?file=/src/App.tsx)
-   [Assista "Intro to re-renders" no YouTube](https://youtu.be/qTDnwmMF5q8)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part2-hooks-example.png)

### ⛔️ Razões para re-renders: mudanças de props (o grande mito)

Não importa se as props do component mudam, ou não, quando se fala em re-renders de components não-memoized.

Para que as props sejam alteradas, elas precisam ser atualizadas pelo parent component. Isso significa que o parent teria que re-render, o que acionaria o re-render do child component, independentemente de suas props.

Somente quando técnicas de memoization são usadas (`React.memo`, `useMemo`), a mudança de props se torna importante.

-   [Veja um exemplo no codesandbox](https://codesandbox.io/s/part-2-5-re-render-props-not-relevant-2b8o0p?file=/src/App.tsx)
-   [Assista "Intro to re-renders" no YouTube](https://youtu.be/qTDnwmMF5q8)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part2-props-myth.png)

## Evitando re-renders com composition

### ⛔️ Antipattern: Criando components na render function

Criar components dentro da render function de outro component é um anti-pattern que pode ser o maior destruidor de desempenho. Em cada re-render, o React irá re-mount este component (ou seja, destruí-lo e recriá-lo do zero), o que será muito mais lento do que um re-render normal. Além disso, isso levará a bugs como:

-   possíveis "flashes" de conteúdo durante os re-renders
-   state sendo redefinido no componente a cada re-render
-   useEffect sem dependências disparadas em cada re-render
-   se um component estiver focused, o focus será perdido

Mais recursos:

-   [Veja um exemplo no codesandbox](https://codesandbox.io/s/part-3-1-creating-components-inline-t2vmkj?file=/src/App.tsx)
-   Leia porque isso acontece em mais detalhes: [React reconciliation: como isso funciona e por que devemos nos importar](https://www.developerway.com/posts/reconciliation-in-react)
-   Assista no YouTube: [Mastering React Reconciliation](https://youtu.be/cyAbjx0mfKM)

### ✅ Evitando re-renders com composition: movendo o state "para baixo"

Esse pattern pode ser benéfico quando um component pesado gerencia o state, e esse state é usado apenas em uma pequena parte isolada da render tree. Um exemplo típico seria abrir/fechar um dialog com um clique de button em um component complicado que renderiza uma parte significativa de uma página.

Nesse caso, o state que controla a aparência do modal dialog, o próprio dialog e o button que aciona a atualização podem ser encapsulados em um component menor. Como resultado, o component maior não será re-render nessas mudanças de state.

- [Veja um exemplo no codesandbox](https://codesandbox.io/s/part-3-2-moving-state-down-vlh4gf?file=/src/App.tsx)
- Leia sobre isso em mais detalhes: [O mistério do React Element, children, parents e re-renders](https://www.developerway.com/posts/react-elements-children-parents)
- Leia sobre reconciliation: [React reconciliation: como isso funciona e por que devemos nos importar](https://www.developerway.com/posts/reconciliation-in-react)
- Ou assista um vídeo do YouTube: [Introdução aos re-renders - Advanced React Course, Episode](https://youtu.be/qTDnwmMF5q8)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part3-moving-state-down.png)

### ✅ Evitando re-renders com composition: children como props

Isso também pode ser chamado de "wrap state around children". Esse pattern é semelhante a "moving state down": ele encapsula mudanças de state em um component menor. A diferença aqui é que o state é usado em um elemento que encapsula uma parte lenta da render tree, portanto não pode ser extraído tão facilmente. Um exemplo típico seria callbacks de `onScroll` ou `onMouseMove` attached ao root element de um component.

Nessa situação, o state management e os components que usam esse state podem ser extraídos para um component menor, e o component lento pode ser passado para ele como `children`. Da perspectiva dos components menores, `children` são apenas props, portanto, não serão afetados pela mudança de state e, portanto, não serão re-render.

-   [Veja um exemplo no codesandbox](https://codesandbox.io/s/part-3-3-children-as-props-59icyq?file=/src/App.tsx)
-   Leia mais sobre composition: [O mistério de React Element, children, parents e re-renders](https://www.developerway.com/posts/react-elements-children-parents)
-   Leia sobre reconciliation: [React reconciliation: como isso funciona e por devemos nos importar](https://www.developerway.com/posts/reconciliation-in-react)
-   Ou assista sobre o pattern no YouTube: [Elements, Children e Re-renders - Advanced React course, Episode 2](https://youtu.be/So6plt0QE_M)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part3-passing-as-children.png)

### ✅ Evitando re-renders com composition: components como props

Praticamente igual ao pattern anterior, com o mesmo comportamento: ele encapsula o state dentro de um component menor e components pesados são passados para ele como props. As props não são afetadas pela mudança de state, portanto, components pesados não serão re-render.

Pode ser útil quando alguns components pesados são independentes do state, mas não podem ser extraídos como children como um grupo.

-   [Veja um exemplo no codesandbox](https://codesandbox.io/s/part-3-4-passing-components-as-props-9h3o5u?file=/src/App.tsx)
-   Leia sobre passar components como props: [React component como prop: a maneira correta](https://www.developerway.com/posts/react-component-as-prop-the-right-way)
-   Leia sobre reconciliation: [React reconciliation: como isso funciona e por que devemos nos importar](https://www.developerway.com/posts/reconciliation-in-react)
-   Ou assista sobre o pattern no YouTube: [Components como props - Advanced React course, Episode 3](https://youtu.be/gEW0Wv0DMso)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part3-passing-as-props.png)

## Evitando re-renders com React.memo

Encapsulando um component em `React.memo` interromperá a cadeia downstream de re-renders que é acionada em algum lugar acima da render tree, a menos que as props deste component tenham mudado.

Isso pode ser útil ao renderizar um component pesado que não depende da origem do re-render (ou seja, state, dados alterados).

-   [Veja um exemplo no codesandbox](https://codesandbox.io/s/part-4-simple-memo-fz4xhw?file=/src/App.tsx)
-   Assista um video no tópico: [Mastering memoization in React - Advanced React course, Episode 5](https://youtu.be/huBxeruVnAM)

### ✅ React.memo: component com props

Todas as props que não são valores primitivos devem ser memoized para que React.memo funcione

-   [Veja um exemplo no codesandbox](https://codesandbox.io/s/part-4-1-memo-on-component-with-props-fq55hm?file=/src/App.tsx)
-   Assista um vídeo no tópico: [Mastering memoization in React - Advanced React course, Episode 5](https://youtu.be/huBxeruVnAM)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part4-memo-with-props.png)


### ✅ React.memo: components como props ou children

`React.memo` deve ser aplicado aos elements passados como children/props. Memoize o parent component não funcionará: children e props serão objetos, portanto, eles mudarão a cada re-render.

-   [Veja um exemplo no codesandbox](https://codesandbox.io/s/part-4-2-memo-on-components-in-props-55tebl?file=/src/App.tsx)
-   Leia mais sobre children e parents: [O mistério de React Element, children, parents e re-renders](https://www.developerway.com/posts/react-elements-children-parents)
-   Assista um vídeo no tópico: [Mastering memoization in React - Advanced React course, Episode 5](https://youtu.be/huBxeruVnAM)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part4-memo-as-props.png)

## Melhorando o desempenho de re-renders com useMemo/useCallback

### ⛔️ Antipattern: useMemo/useCallback desnecessários em props

Memoizing props por sí só não impedirá re-renders de um child component. Se um parent component re-renders, ele acionará o re-render de um child component independentemente de suas props.

-   [Veja um exemplo no codesandbox](https://codesandbox.io/s/part-5-1-unnecessary-usememo-lmk8fq?file=/src/App.tsx)
-   Leia mais detalhes em: [Como utilizar useMemo e useCallback: você pode remover a maioria deles](https://www.developerway.com/posts/how-to-use-memo-use-callback)
-   Assista um video no tópico: [Mastering memoization in React - Advanced React course, Episode 5](https://youtu.be/huBxeruVnAM)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part5-unnecessary-usememo-on-props.png)

### ✅ useMemo/useCallback necessário

Se um child component é encapsulado em `React.memo`, todas as props que não são valores primitivos deverão ser memoized

-   [Veja um exemplo no codesandbox](https://codesandbox.io/s/part-5-2-usememo-in-props-trx97x?file=/src/App.tsx)
-   Assista um vídeo no tópico: [Mastering memoization in React - Advanced React course, Episode 5](https://youtu.be/huBxeruVnAM)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part5-necessary-usememo-props.png)

Se um component usa valor não-primitivo como dependência em hooks como `useEffect`, `useMemo`, `useCallback`, ele deve ser memoized.

[Veja um exemplo no codesandbox](https://codesandbox.io/s/part-5-2-usememo-in-effect-88tbov)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part5-necessary-usememo-dep.png)

### ✅ useMemo para cálculos pesados

Um dos casos de uso `useMemo` é evitar cálculos caros em cada re-render.

`useMemo` tem seu custo (consome um pouco de memória e torna o render inicial um pouco mais lento), portanto não deve ser usado em todos os cálculos. No React, mounting e updating components será o cálculo mais caro na maioria dos casos (a menos que você esteja realmente calculando números primos, o que você não deveria fazer no frontend de qualquer maneira).

Como resultado, o caso de uso típico `useMemo` seria memoize React elements. Geralmente partes de uma render tree existente ou resultados de uma render tree gerada, como uma map function que retorna novos elements.

O custo de "pure" javascript operations, como ordenar ou filtering um array, geralmente é insignificante, em comparação com atualizações de components.

-   [Veja um exemplo no codesandbox](https://codesandbox.io/s/part-5-3-usememo-for-expensive-calculations-trx97x?file=/src/App.tsx)
-   Assista um vídeo no tópico: [Mastering memoization in React - Advanced React course, Episode 5](https://youtu.be/huBxeruVnAM)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part5-necessary-usememo-complex.png)

## Melhorando o desempenho de re-render de listas

Além das regras regulares e patterns de re-render, o `key` attribute pode afetar o desempenho das listas no React.

__Importante:__ apenas fornecer `key` attributes não melhorará o desempenho das listas.

Para evitar rerenders de list elements, você precisa encapsular eles com `React.memo` e seguir todas as práticas recomendadas.

O valor em `key` deve ser uma string consistente entre os re-renders para cada elemento da lista. Normalmente, o `id` do item ou o `index` de arrays são usados para isso.

Não há problema em usar `index` de arrays como key, se a lista for estática , ou seja, os elementos não são adicionados/removidos/inseridos/reordenados.

Usar o index do array em listas dinâmicas pode levar a:

-   bugs se os itens tiverem estado ou quaisquer uncontrolled elements (como form inputs)
-   desempenho degradado se os itens forem agrupados em React.memo

Recursos adicionais:

-   Leia sobre keys em mais detalhes: [React key attribute: melhores práticas para listas performáticas](https://www.developerway.com/posts/react-key-attribute).
-   Leia sobre reconciliation: [React reconciliation: como isso funciona e por que devemos nos importar](https://www.developerway.com/posts/reconciliation-in-react)
-   Leia sobre reconciliation: [Mastering React Reconciliation - Advanced React course, Episode 6](https://youtu.be/cyAbjx0mfKM)
-   [Veja um exemplo em codesandbox - lista estática](https://codesandbox.io/s/part-6-static-list-with-index-and-id-as-key-7i0ebi?file=/src/App.tsx)
-   [Veja um exemplo em codesandbox - lista dinâmica](https://codesandbox.io/s/part-6-dynamic-list-with-index-and-id-as-key-s50knr?file=/src/App.tsx)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part6-lists-example.png)

### ⛔️ Antipattern: valor randômico como key em listas

Valores gerados randomicamente nunca deveria ser usados como valores no `key` attribute em listas. Elas levarão ao re-mounting de itens do React em cada nova renderização, o que levará a:

-   desempenho muito ruim da lista
-   bugs se os itens tiverem state ou quaisquer uncontrolled elements (como form inputs)

[Veja um exemplo no codesandbox](https://codesandbox.io/s/part-6-1-random-values-in-keys-z1zhy6?file=/src/App.tsx)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part6-lists-antipattern.png)

## Evitando re-renders causados pelo Context

### ✅ Evitando Context re-renders: memoizing o valor do Provider

Se o Context Provider não for colocado na raiz do app e houver a possibilidade de ele re-render devido a alterações em seus ancestrais, seu valor deverá ser memoized.

[Veja um exemplo no codesandbox](https://codesandbox.io/s/part-7-1-memoize-context-provider-value-qgn0me?file=/src/App.tsx)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part7-context-provider-memo.png)

### ✅ Evitando Context re-renders: splitting de dados e API

Se no contexto houver uma combinação de dados e API (getters e setters), eles poderão ser "split" (divididos) em diferentes providers no mesmo component. Dessa forma, os components que usam apenas API não serão re-render quando os dados forem alterados.

Leia mais sobre esse pattern aqui: [Como escrever React apps de alto desempenho com Context](https://www.developerway.com/posts/how-to-write-performant-react-apps-with-context)

[Veja um exemplo no codesandbox](https://codesandbox.io/s/part-7-2-split-context-data-and-api-r8lsws?file=/src/App.tsx)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part7-context-split-api.png)

### ✅ Evitando Context re-renders: splitting dados em chunks

Se o Context gerenciar alguns chunks de dados independentes, eles poderão ser divididos em providers menores no mesmo provider. Dessa forma, apenas os consumers do chunk alterado serão re-render.

Leia mais sobre esse pattern aqui: [Como escrever React apps de alto desempenho com Context](https://www.developerway.com/posts/how-to-write-performant-react-apps-with-context)

[Veja um exemplo no codesandbox](https://codesandbox.io/s/part-7-3-split-context-into-chunks-dbg20m?file=/src/App.tsx)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part7-context-split-data.png)

### ✅ Evitando Context re-renders: Context selectors

Não há como evitar que um component, que usa uma parte do valor do Context, seja re-render, mesmo que o dado usado não tenha sido alterado, mesmo com o `useMemo` hook.

Context selectors, no entanto, podem ser "faked" com o uso de higher-order components e `React.memo`.

Leia mais sobre esse pattern aqui: [Higher-Order Components na era dos React Hooks](https://www.developerway.com/posts/higher-order-components-in-react-hooks-era)

[Veja um exemplo no codesandbox](https://codesandbox.io/s/part-7-4-context-selector-lc8n5g?file=/src/App.tsx)

![código de exemplo](https://www.developerway.com/assets/react-re-renders-guide/part7-context-selectors.png)

### Fonte

[React re-renders guide: everything, all at once Makarevich](https://www.developerway.com/posts/react-re-renders-guide) - por Nadia Makarevich