Não é possível atualizar o estado do React em um componente desmontado: como corrigir e acelerar o código

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

O bug familiar que irrita todo desenvolvedor React

\n

Você já viu no console do navegador o aviso Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application.? Se sim — você não está sozinho. Esta pergunta no Stack Overflow acumulou milhares de visualizações e se tornou uma verdadeira dor de cabeça para iniciantes e desenvolvedores experientes em React.

\n

Neste artigo, vamos entender por que esse erro ocorre, como corrigi-lo usando TypeScript e, principalmente, como otimizar o desempenho da sua aplicação para que esses avisos não apareçam mais. Vamos lá!

\n\n

Por que o React reclama sobre um componente desmontado?

\n

Imagine: o usuário abriu uma página, iniciou o carregamento de dados (por exemplo, via fetch ou axios), mas não esperou a resposta e navegou para outra tela. O componente que iniciou a requisição já foi desmontado (removido do DOM), mas a operação assíncrona ainda está em execução. Quando a resposta chega, o código tenta atualizar o estado (state) de um componente que já não existe. O React reclama com razão: "Ei, não posso atualizar algo que não existe!".

\n

Em TypeScript, esse problema é especialmente perceptível se você usa tipagem estrita e esquece as funções de cleanup no useEffect.

\n\n

Exemplo clássico do problema (TypeScript)

\n
import React, { useState, useEffect } from 'react';\n\ninterface User {\n  id: number;\n  name: string;\n}\n\nconst UserProfile: React.FC<{ userId: number }> = ({ userId }) => {\n  const [user, setUser] = useState<User | null>(null);\n\n  useEffect(() => {\n    fetch(`/api/users/${userId}`)\n      .then(res => res.json())\n      .then(data => setUser(data)); // Erro! O componente pode estar desmontado\n  }, [userId]);\n\n  return <div>{user?.name}</div>;\n};
\n

Se o usuário mudar rapidamente de rota, o componente UserProfile será removido e o setUser será chamado em vão. Obtemos um vazamento de memória e um erro no console.

\n\n

3 formas de corrigir o erro (com exemplos em TypeScript)

\n

Vamos analisar três abordagens confiáveis que resolvem o problema e melhoram o desempenho da aplicação React.

\n\n

Forma 1: Flag usando useRef (a mais popular)

\n

Criamos uma flag ref que indica se o componente está montado. Atualizamos o estado apenas se a flag for true.

\n
import React, { useState, useEffect, useRef } from 'react';\n\nconst UserProfile: React.FC<{ userId: number }> = ({ userId }) => {\n  const [user, setUser] = useState<User | null>(null);\n  const isMounted = useRef(true);\n\n  useEffect(() => {\n    // Redefinimos a flag a cada montagem\n    isMounted.current = true;\n\n    fetch(`/api/users/${userId}`)\n      .then(res => res.json())\n      .then(data => {\n        if (isMounted.current) {\n          setUser(data);\n        }\n      });\n\n    // Cleanup: define a flag como false ao desmontar\n    return () => {\n      isMounted.current = false;\n    };\n  }, [userId]);\n\n  return <div>{user?.name}</div>;\n};
\n

Vantagens: simplicidade, não requer bibliotecas adicionais. Desvantagem: é preciso lembrar de fazer o cleanup.

\n\n

Forma 2: AbortController para cancelar a requisição fetch (recomendada)

\n

Uma abordagem moderna e correta é cancelar a própria requisição, em vez de apenas ignorar a resposta. Isso economiza recursos do servidor e do cliente.

\n
import React, { useState, useEffect } from 'react';\n\nconst UserProfile: React.FC<{ userId: number }> = ({ userId }) => {\n  const [user, setUser] = useState<User | null>(null);\n  const [loading, setLoading] = useState(true);\n\n  useEffect(() => {\n    const controller = new AbortController();\n    const signal = con                

Blogs

Book Recommendations