Conexões Supabase Realtime: Evitando Memory Leaks com WebSockets no React
A Mágica e o Perigo do Tempo Real
O Supabase Realtime permite que você escute mudanças no banco de dados (PostgreSQL) instantaneamente via WebSockets. Se uma nova mensagem for inserida no chat ou o status de um pagamento mudar para "Concluído", a tela do usuário atualiza sozinha em milissegundos — sem a necessidade de recarregar a página ou fazer consultas repetitivas (long-polling).
Apesar de ser uma ferramenta incrivelmente poderosa, ela possui uma armadilha fatal para desenvolvedores inexperientes: O Vazamento de Memória (Memory Leak).
O Erro Amador: Sockets Zumbis
Quando você manda o navegador do cliente abrir um canal WebSocket, ele cria uma conexão TCP contínua, pesada e persistente com o servidor do Supabase.
O maior erro em aplicações Single Page (SPA) como Next.js ou React é assinar o canal e não avisar o navegador que ele deve ser destruído quando o usuário sai da página.
Como NÃO Fazer ❌
Se você escrever o código de escuta sem uma estratégia de limpeza, o vazamento acontece instantaneamente:
// ARQUITETURA PROIBIDA: Sem Cleanup
useEffect(() => {
// O usuário entra na página e abre um Socket.
supabase
.channel('room_1')
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages' }, (payload) => {
console.log('Nova mensagem', payload);
})
.subscribe();
// Se o usuário clicar em "Voltar" e depois "Entrar" novamente,
// um SEGUNDO socket é aberto, rodando em paralelo com o primeiro.
}, []);
O Resultado: Se o usuário navegar por 10 telas diferentes que assinam esse canal, o computador dele ficará com 10 conexões abertas em paralelo, processando e duplicando os mesmos dados até travar o navegador (Out of Memory) e esgotar os limites de conexão simultânea da sua conta Supabase.
A Arquitetura Padrão Ouro: Cleanups no React
No React, o useEffect possui um mecanismo de autodefesa chamado Cleanup Function. Sempre que o componente for destruído (ou seja, quando o usuário sair da página ou a tela mudar), o React vai rodar a função que você retornar lá no final.
Para que sua aplicação escale sem bugs e vazamentos de memória, é obrigatório desinscrever e remover o canal do Supabase removeChannel() antes que a tela suma.
Como FAZER ✅
Copie esta estrutura para qualquer implementação de Realtime. É a única abordagem permitida em nossa engenharia:
import { useEffect, useState } from 'react';
import { createClient } from '@/utils/supabase/client';
export function RealtimeChat() {
const [messages, setMessages] = useState([]);
const supabase = createClient();
useEffect(() => {
// 1. Criamos a referência para a conexão
const channel = supabase
.channel('chat_publico')
.on('postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'messages' },
(payload) => {
// Atualiza o estado com o novo dado do banco
setMessages((prev) => [...prev, payload.new]);
})
.subscribe();
// 2. A Regra de Ouro: Função de Limpeza (Cleanup)
return () => {
// Quando o componente for desmontado (usuário mudar de página),
// matamos a conexão TCP WebSocket instantaneamente.
supabase.removeChannel(channel);
};
}, []); // Array vazio garante que o setup e o cleanup rodem 1 vez.
return (
<div>
{/* Renderiza a UI... */}
</div>
);
}
Resumo Operacional
- Abra o Canal: Use
supabase.channel().subscribe(). - Guarde na Memória: Armazene esse canal em uma variável (
const channel). - Mate a Conexão: Retorne a função de cleanup do
useEffectexecutandosupabase.removeChannel(channel).
Com essa disciplina arquitetural, seu SaaS pode ter 100 mil usuários simultâneos usando Realtime, pois cada cliente fechará a porta ao sair, mantendo a memória do navegador imaculada e os servidores aliviados.