Olá, estudante da Alura. Sou o instrutor Pedro.
Audiodescrição: Pedro é uma pessoa branca, com cabelos escuros e barba escura. Ele veste roupas pretas em um ambiente iluminado pelas cores azul e rosa.
Seja bem-vindo a mais um curso de React na plataforma da Alura. Recentemente, recebi uma ligação do pessoal da Zoop, um e-commerce de multi-departamento que oferece eletrônicos, moda, itens para casa e jardim, entre outros. Trata-se de uma mega-store que está enfrentando problemas de performance na aplicação. Eles relataram que a aplicação trava em alguns momentos e apresenta problemas de carregamento. Por isso, entraram em contato conosco, especialistas em resolver problemas e ensinar através deles, para ver se conseguimos ajudá-los.
Este curso faz parte de uma sequência de conhecimento que é essencial para avançarmos nesta etapa. É imprescindível já conhecer o funcionamento do TypeScript, do React, o funcionamento de componentes por classe, o gerenciamento de estado a partir do context, e também a utilização de hooks e hooks customizados. Vamos focar bastante na parte de performance utilizando ferramentas que serão apresentadas ao longo do curso.
Neste curso, vamos entender como funciona o ciclo de desenvolvimento dentro do React, passando pelo ciclo de vida de um componente, como ocorre a comparação de elementos para serem renderizados em tela, como o React entende e administra isso, e também formas de otimizar através de ferramentas do próprio React para lidar com problemas de renderização, como a renderização em cascata. Vamos explorar esses conselhos dentro da nossa sequência de vídeos.
Além disso, conheceremos duas ferramentas muito úteis para gerar relatórios e entender problemas de performance e gargalos dentro da aplicação. Faremos tudo do zero, desde a configuração e instalação, consultando a documentação para entender o funcionamento. Tudo está preparado com muito cuidado para avançarmos passo a passo e concluirmos a jornada com um projeto bem estruturado e relatórios positivos sobre o Zoop.
No final, vamos entender o futuro da otimização de performance que a equipe do React preparou, ouvindo a comunidade, como sempre fazem. Esperamos que continuem escutando, pois essas melhorias têm sido muito positivas para nós, enquanto pessoas desenvolvedoras.
Preparem-se, pois na sequência vamos entrar na parte de entender os conceitos do ciclo de vida de um componente e como isso afeta as renderizações. Nos vemos na sequência para começarmos com entusiasmo e compreendermos as demandas do Zoop. Até lá!
Antes de entrarmos na parte do código, vamos entender como funciona a questão de renderização e os custos de performance dentro das nossas aplicações React, especialmente quando tratamos do tema de renderizações. Primeiramente, precisamos compreender o ciclo de vida de um componente. No contexto do React, quando falamos de estrutura de renderização e telas, a menor estrutura que temos dentro de uma página é justamente um componente.
Antes de discutirmos como ocorre a renderização no React, é importante entender o que acontece com um componente desde o momento em que é criado até o momento em que é desmontado ou removido da tela. Na tela, estamos exibindo um modelo simplificado com as três etapas principais de um componente: a montagem (ou mounting), a atualização (updating) e a desmontagem (unmounting). Essa imagem foi adaptada para a nossa paleta de cores, mas pode ser encontrada na documentação do React. Optamos por apresentá-la aqui para facilitar a compreensão.
Para que um componente seja montado na tela, ele passa por três funções que permitem sua montagem, ou seja, sua renderização e o início do ciclo de vida. A primeira etapa é quando o componente passa a existir dentro do DOM, sendo renderizado na tela. O constructor
é uma função dos componentes em classe do React, que continua existindo mesmo em componentes funcionais. Nos componentes funcionais, o constructor
, render
e outras funções dos componentes em classe são encapsulados pelo React para permitir a renderização funcional com as mesmas vantagens dos componentes de classe.
Vamos começar com a primeira etapa do ciclo de vida, que é a montagem do componente. Aqui estão as funções principais envolvidas:
constructor()
render()
componentDidMount()
O constructor
recebe as propriedades principais do componente. A função render
, que hoje é representada pelo return
nos componentes funcionais, determina o que será exibido na tela. A terceira função é o componentDidMount
, que atualmente é relacionado ao useEffect
, a função de efeito colateral dentro do React. Embora estejamos falando de componentes, isso também se aplica a estruturas maiores, como páginas ou views aninhadas.
Com as funções constructor
, render
e componentDidMount
, temos o ciclo inicial de renderização do componente na tela. No constructor
, definimos propriedades e funções; no render
, determinamos o que será exibido; e no componentDidMount
, executamos operações após a montagem do componente na tela, como no caso do useEffect
.
Após a fase de montagem, temos a fase de atualização (updating), que pode ou não ocorrer, dependendo de alterações nas props
ou no state
do componente. Se não houver alterações, não há atualização. Quando ocorre uma atualização, o render
é chamado novamente, e o componentDidUpdate
é executado para garantir que o componente foi renderizado e montado no DOM. Nesse momento, podemos executar efeitos colaterais.
render()
componentDidUpdate()
Após a fase de atualização, temos a fase de desmontagem (unmounting). Quando queremos remover um componente da tela, seja por navegação ou condições específicas, utilizamos o componentWillUnmount
. Essa função faz parte do ciclo de vida e verifica se o componente será desmontado. O React, então, trata de remover o elemento do DOM através de um algoritmo de comparação interno, sem que precisemos nos preocupar com isso. Se o componente for desmontado, ele é removido da memória e do DOM visível, mas não do DOM virtual do React.
componentWillUnmount()
Resumindo o ciclo de vida de um componente: ele é montado na tela, pode ou não receber atualizações que geram novas renderizações, e é desmontado a partir do componentWillUnmount
, onde são feitas as validações e a destruição do componente na tela.
Lembramos que estamos discutindo o contexto de classes, pois atualmente, em nossos componentes funcionais, não precisamos lidar com essas funções diretamente. Isso ocorre porque tudo está encapsulado dentro do próprio React, que evoluiu significativamente. Percebemos o quanto a chegada dos hooks aliviou nosso trabalho, assim como as funções que temos dentro do React para construir nossos componentes em tela.
Esta é uma estrutura simples de como funciona o ciclo de vida de um componente. A partir disso, temos um exemplo mais prático. Suponhamos que temos nosso aplicativo ao centro, e nele, dois renders diferentes, ou seja, dois renders filhos do nosso aplicativo. Um deles está gerando um inspiration generator, que pode ser qualquer componente, e, como irmão desse inspiration generator, está renderizando nosso componente fancy text. Dentro do inspiration generator, temos mais dois filhos: a renderização do fancy text e a renderização do copyright à direita. Esta é uma estrutura simples para ilustrar o funcionamento mais detalhado de como isso seria dentro de uma aplicação, não apenas no esquema de montagem, atualização e desmontagem, mas já trazendo para um contexto de aplicativo para avançarmos mais na questão de renderização.
O que acontece é que, como temos componentes alinhados, a re-renderização ocorre em cascata. Quando temos uma renderização, voltando ao nosso esquema de montagem, atualização e desmontagem, ela acontece em forma de cascata. Se temos um componente pai que foi renderizado, ele também renderizará seus componentes filhos. Ou seja, se houver uma alteração no componente pai, como no caso do inspiration generator, também haverá uma renderização de seus filhos. Isso garante que nossa interface reflita o estado mais recente, evitando defasagem de propriedades, estados e dados que estão transitando dentro dos nossos componentes.
Para ilustrar, quando o inspiration generator é atualizado, ele também renderiza o fancy text e o copyright que estão alinhados a ele. O componente à direita do inspiration generator, o outro fancy text, não sofre alteração porque não está alinhado ao inspiration generator, ou seja, não é um componente filho e, portanto, não participa dessa renderização em cascata.
Quando temos essa renderização em cascata, que é do próprio React, não há problema na forma como isso funciona. No entanto, quando começamos a falar em tópicos de performance e estabilidade, enfrentamos um problema crítico: renderizações desnecessárias. Isso ocorre porque o React não sabe quais filhos dependem ou não daquele estado que foi alterado. Assim, quando o inspiration generator sofre uma alteração, o React não sabe se o fancy text e o copyright dependem ou não daquela propriedade ou estado alterado. Para evitar problemas e garantir que a interface reflita o estado mais recente, ele renderiza os dois novamente, mesmo que isso não seja necessário.
Começamos a enfrentar problemas significativos quando temos muitos componentes alinhados sofrendo com essa parte de renderização em cascata. Por exemplo, no código, temos uma função App
com dois useState
de counter
, count1
e count2
, que são separados, e no return
temos nosso título. Vamos trazer isso para nossa estrutura, lembrando da forma como funciona por baixo dos panos. Temos o constructor
, o render
e o componentDidMount
. Na renderização do código, nosso useState
, count1
e count2
, seriam nosso constructor
, e a função de return
seria nosso render
.
Aqui está um exemplo de como isso é implementado em um componente funcional:
// Componente pai renderizando múltiplos filhos
function App() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
return (
<>
<Title /> {/* Título estático */}
<Counter count={count1} onClick={() => setCount1(count1 + 1)} />
<Counter count={count2} onClick={() => setCount2(count2 + 1)} />
</>
);
}
A cada clique, como não temos componentWillMount
, componentDidUpdate
, nada disso em nossos componentes de função, ao executar o onClick
, por exemplo, no counter
, seja count1
ou count2
, dentro do useState
, ele aciona o componentDidMount
, componentDidUpdate
e assim por diante, pela lógica existente no useState
. Se alterarmos o count1
, o filho sofre uma alteração, mas o React não sabe que o counter
, nesse caso, teve essa alteração. O React não sabe se todos os outros componentes dependem ou não daquele estado ou propriedade. Como o estado está dentro do App
, e não é interno ao counter
ou ao count2
, por exemplo, ao fazer uma alteração direta no count1
, que está dentro do estado interno do App
, ele renderiza todo o componente App
e todos os filhos, pois o React realmente não sabe se, por exemplo, há outro counter
utilizando o count1
, ou se estamos utilizando o count1
para alguma informação abaixo. Por isso, ele verifica a alteração e renderiza tudo o que está dentro, facilitando o problema e mantendo a coesão dos dados sendo passados.
Começamos a perceber impactos visíveis dentro da nossa aplicação, principalmente quando lidamos com listas, como será o caso do nosso Zoop
, onde teremos várias listagens e componentes que enfrentarão problemas de carregamento por conta disso. A re-renderização de componentes não afetados pelo estado acaba degradando a performance da nossa aplicação, prejudicando nosso andamento durante o desenvolvimento. Se temos muitas listas, muitos componentes com gráficos, e fazemos uma alteração que não necessariamente precisa ser renderizada novamente, enfrentamos uma degradação da performance e relatos de usuários dizendo que a aplicação está lenta e as imagens demoram para carregar. Talvez já tenhamos ouvido isso em nosso local de trabalho, estágio ou projeto em desenvolvimento. Isso é muito comum e um dos erros que enfrentamos no dia a dia, especialmente lidando com informações de um time de marketing, onde há muitas alterações, principalmente quando eventos grandes estão chegando. O fluxo de alterações de informações é muito grande. Se a aplicação não estiver preparada e utilizando os recursos corretos para lidar com isso, o resultado é uma aplicação que começa a travar e relatos negativos de performance que precisamos considerar.
Vamos começar entendendo como está funcionando o projeto antes de analisarmos o código. Para isso, é importante verificarmos o arquivo package.json
da aplicação, que nos mostra como as coisas estão configuradas. Neste momento, estamos compartilhando o editor de texto e abrindo o arquivo package.json
, localizado na raiz do projeto.
Aqui está o conteúdo do arquivo package.json
:
{
"name": "react-zoop-ecomm",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint:fix": "eslint . --ext ts,tsx --fix",
"format": "prettier --write \"src/**/*.{ts,tsx,jsx,json,css,md}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx,jsx,json,css,md}\"",
"preview": "vite preview",
"server": "json-server --watch server.json --port 3001"
},
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@mui/icons-material": "^7.2.0",
"@mui/material": "^7.2.0",
"@types/react-router-dom": "^5.3.3",
"json-server": "^1.0.0-beta.3",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-icons": "^5.5.0",
"react-router-dom": "^7.6.3"
},
"devDependencies": {
"@eslint/js": "^9.30.1",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^4.6.0",
"autoprefixer": "^10.4.21",
"eslint": "^9.30.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.3.0",
"postcss": "^8.5.6",
"prettier": "^3.2.5",
"tailwindcss": "^3.4.17",
"typescript": "^5.8.3",
"typescript-eslint": "^8.35.1",
"vite": "^7.0.3"
}
}
Caso desejem fazer um checkout da branch e acompanhar o desenvolvimento do projeto durante a aula, o link para acessar o repositório e a branch da aula está sempre no topo do terminal integrado. Para seguir o código da aula, basta verificar o topo do terminal. Se preferirem acessar o repositório após a finalização das aulas, o link é disponibilizado no início de cada nova aula, junto com o material das aulas passadas.
O projeto está utilizando o Vite, conforme indicado pelas dependências, e o React na versão 19.1, que é a mais recente. Vamos abordar um detalhe interessante que o React 19 disponibiliza a partir dessa versão. Observamos que na linha 22 está sendo utilizado o JSON Server, indicando que há um back-end funcionando localmente, provavelmente através de um arquivo .json
, que no caso é o server.json
na raiz do projeto.
A configuração é padrão para um projeto Vite, com TypeScript, ESLint, PostCSS, Prettier e Tailwind. Além do ESLint, temos os autofixers funcionando, o que demonstra que o projeto possui um certo nível de maturidade e foi bem desenvolvido, oferecendo algumas seguranças para a pessoa desenvolvedora. Além disso, está utilizando bibliotecas como PostCSS, Material e Emotion.
Nos comandos, verificamos que o Vite está sendo utilizado para o build. Na linha 14, temos o comando para rodar o servidor local e o run dev
para executar o projeto. Localmente, vamos digitar npm run server
e duplicar uma aba para digitar npm run dev
, permitindo visualizar o projeto rodando no navegador.
npm run server
npm run dev
Após dar um refresh na página, estamos exibindo o Google Chrome no localhost:5173
, que é a porta onde o projeto está rodando. Observamos que algumas imagens carregam e outras não, o que pode indicar um problema na configuração das imagens. Os componentes estão bem desenvolvidos, com páginas como "nossa história", "sobre nós", "carrinho" (mostrando o estado de carrinho vazio) e "favoritos" (ainda sem produtos favoritos).
No entanto, ao voltar para a tela inicial da aplicação Zoop, notamos uma demora no carregamento das imagens, o que não é um comportamento esperado para uma aplicação de e-commerce. Quando fomos contatados para trabalhar nesse projeto, identificamos problemas de performance, especialmente no tratamento de imagens. O carregamento da listagem está um pouco estranho.
Ao explorar mais, clicamos em uma categoria, como "esportes", e notamos que a lista demora para renderizar as imagens. A listagem não é muito grande, mas preocupa a falta de uma tratativa de paginação ou lazy load, que é um conceito utilizado para exibir informações conforme são renderizadas na tela, auxiliando na virtualização e poupando problemas de renderização.
Detectamos problemas de renderização e re-renderizações desnecessárias, principalmente pelo tempo que as imagens levam para carregar. Ao favoritar um item, ele atualiza na aba "sobre nós" no topo do header, próximo ao ícone do carrinho. O pop-up de desconto está funcionando bem, mas ao fechar, notamos que os itens mais procurados da semana, como bicicleta, sabonete, sapato social e camisa social, mudam ao clicar em "ganhar desconto".
Jogo de panelas, livro de história, bola de futebol americano e bola de golfe. Vamos fechar o pop-up de desconto. Agora, mudou novamente: livro de autoajuda, cortina blackout, sofá de três lugares e livro de arte.
Temos aqui um problema clássico de renderização em cascata. Provavelmente, na estrutura que vamos analisar em mais detalhes em breve, qualquer alteração no estado nesta página principal está causando efeitos colaterais em toda a página. Como se trata de uma listagem de itens e possivelmente um retorno aleatório de alguns itens, o estado não está sendo mantido adequadamente para os componentes aninhados, o que resulta em um problema de renderização.
Como vimos no vídeo passado, ao entender mais sobre esses problemas, conseguimos identificar alguns pontos importantes para começarmos a trabalhar. Vamos voltar ao editor de texto, fechar o package.json
por enquanto e minimizar os terminais integrados do editor.
No diretório src
, temos a estrutura atual do nosso componente. A pasta "assets" contém os recursos, a pasta "componentes" contém alguns componentes, e a pasta "context" contém as APIs de contexto que funcionam internamente na aplicação. Temos também os hooks separados para useCart
, categorias
e favoritos
.
A página que estamos procurando é a nossa home. Vamos abri-la antes de mexer em outras partes. Na linha 11, temos um componente funcional padrão: export function Home
. Aqui, há gerenciamento de estados, como showWelcome
, e já conseguimos identificar problemas semelhantes aos que vimos no slide.
Lembram-se do exemplo do count
, count1
e count2
? O que acontece é que, na home, temos vários outros componentes alinhados. Quando fazemos uma alteração no estado do showWelcome
, que é uma alteração interna de estado, ele renderiza novamente todos os componentes filhos, graças à forma como o React trabalha no algoritmo de comparação.
Analisando mais a fundo, temos outro gerenciamento de estado para a categoria selecionada, um useNavigate
, um useCategories
, que são hooks customizados, e funções como randomOpen
e randomClose
. Há várias funções que estão sendo executadas como ifs, chamadas imediatamente após a inicialização. Qualquer alteração nesta página, como nos filtros, faz com que todas essas funções, como a de produtos aleatórios na linha 42 e a de produtos na linha 49, sejam renderizadas novamente devido à forma como estão implementadas.
Vamos fazer uma alteração para testar o randomProduct
. Vou remover o if na linha 47, que é a função que estamos chamando novamente, e na linha 42 também vou remover o símbolo do if. Agora, na linha 42, temos apenas o randomProducts
. Precisamos alterar onde ele está sendo utilizado, que será na linha 37. Em vez de uma concha, agora estamos passando como uma função. Vamos atualizar.
Voltando à página, observamos que agora não funciona mais. Isso ocorre porque a forma como desenvolvemos a função e a lógica utilizada, provavelmente fornecida pelo pessoal da OPCON, está interligada em dependências. O products.length
está retornando zero em algum momento, e a função agora é chamada apenas uma vez. Vou adicionar um console.log
para identificar rapidamente.
No console do navegador, ao atualizar a página, percebemos que a função handleProducts
não está sendo chamada da forma que colocamos. Na verdade, o handleProducts
está na linha 337, mas alteramos na linha 338, e também precisamos alterar na linha 336.
Após atualizar novamente, observamos que a função foi chamada quatro vezes no total, mas ainda exibe "nenhum produto encontrado" nos mais procurados da semana. Isso ocorre porque, em algum momento, a função não encontra produtos devido à forma como está filtrando. Ao remover os filtros, os mais procurados da semana exibem as informações. Se alterarmos o estado novamente, ele faz essas alterações.
A forma como o RenderProducts
foi desenvolvido depende dos produtos. Toda vez que algo é alterado na aplicação, os produtos são renderizados novamente, chamando a função RenderProducts
com os produtos alterados. Vamos começar a entender algumas estratégias para evitar problemas como esse na aplicação.
Esse foi apenas um exemplo. Por enquanto, deixaremos o código assim, pois está funcionando da mesma forma que antes. Vamos fazer uma pausa rápida para continuar investigando o que está acontecendo no código.
O curso React: técnicas avançadas de otimização e desempenho possui 281 minutos de vídeos, em um total de 43 atividades. Gostou? Conheça nossos outros cursos de React em Front-end, ou leia nossos artigos de Front-end.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você participa de eventos exclusivos, pode tirar dúvidas em estudos colaborativos e ainda conta com mentorias em grupo com especialistas de diversas áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Para estudantes ultra comprometidos atingirem seu objetivo mais rápido.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.
Conecte-se ao mercado com mentoria personalizada, vagas exclusivas e networking estratégico que impulsionam sua carreira tech para o próximo nível.