Recursos em tempo real — chat ao vivo, notificações, edição colaborativa, painéis ao vivo — exigem uma conexão persistente. Em Reagir,Soquete.io é a maneira mais prática de adicionar funcionalidade em tempo real baseada em WebSocket. Este guia mostra como integrá-lo de forma limpa com ganchos React.
📋 Table of Contents
Configuração
npm install socket.io-client
Conexão Básica
// socket.js - create a single shared socket instance
import { io } from 'socket.io-client';
export const socket = io('http://localhost:3001', {
autoConnect: false, // connect manually when ready
reconnection: true,
reconnectionDelay: 1000,
});
Um gancho useSocket reutilizável
// hooks/useSocket.js
import { useEffect, useState } from 'react';
import { socket } from '../socket';
export function useSocket() {
const [isConnected, setIsConnected] = useState(socket.connected);
useEffect(() => {
socket.connect();
function onConnect() { setIsConnected(true); }
function onDisconnect() { setIsConnected(false); }
socket.on('connect', onConnect);
socket.on('disconnect', onDisconnect);
// Cleanup - CRITICAL to prevent duplicate listeners
return () => {
socket.off('connect', onConnect);
socket.off('disconnect', onDisconnect);
socket.disconnect();
};
}, []);
return { socket, isConnected };
}
Ouvindo Eventos
// hooks/useMessages.js - subscribe to a specific event
import { useEffect, useState } from 'react';
import { socket } from '../socket';
export function useMessages() {
const [messages, setMessages] = useState([]);
useEffect(() => {
function onMessage(msg) {
setMessages(prev => [...prev, msg]);
}
socket.on('message', onMessage);
// Always clean up the listener to avoid duplicates on re-render
return () => {
socket.off('message', onMessage);
};
}, []);
const sendMessage = (text) => {
socket.emit('send_message', { text, timestamp: Date.now() });
};
return { messages, sendMessage };
}
Um componente completo de bate-papo
import { useState } from 'react';
import { useSocket } from './hooks/useSocket';
import { useMessages } from './hooks/useMessages';
function Chat() {
const { isConnected } = useSocket();
const { messages, sendMessage } = useMessages();
const [input, setInput] = useState('');
const handleSend = (e) => {
e.preventDefault();
if (input.trim()) {
sendMessage(input);
setInput('');
}
};
return (
<div>
<div>Status: {isConnected ? 'Connected' : 'Disconnected'}</div>
<ul>
{messages.map((msg, i) => (
<li key={i}>{msg.text}</li>
))}
</ul>
<form onSubmit={handleSend}>
<input value={input} onChange={e => setInput(e.target.value)} />
<button type="submit">Send</button>
</form>
</div>
);
}
Indicadores de digitação e presença
export function useTyping() {
const [typingUsers, setTypingUsers] = useState([]);
useEffect(() => {
function onTyping({ user, isTyping }) {
setTypingUsers(prev =>
isTyping
? [...new Set([...prev, user])]
: prev.filter(u => u !== user)
);
}
socket.on('user_typing', onTyping);
return () => socket.off('user_typing', onTyping);
}, []);
const setTyping = (isTyping) => {
socket.emit('typing', { isTyping });
};
return { typingUsers, setTyping };
}
Lidando com a reconexão
useEffect(() => {
function onReconnect() {
console.log('Reconnected - re-syncing state');
// Re-fetch missed data or re-join rooms after reconnection
socket.emit('rejoin_room', { roomId });
}
socket.io.on('reconnect', onReconnect);
return () => socket.io.off('reconnect', onReconnect);
}, [roomId]);
// Socket.io auto-reconnects; use this hook to re-sync after connection drops
A regra crítica: sempre limpe os ouvintes
O bug mais comum do Socket.io + React são manipuladores de eventos duplicados. Cada vez que um componente é renderizado ou remontado, se você adicionar um ouvinte sem remover o antigo, você acumula duplicatas – uma única mensagem aciona seu manipulador várias vezes. Sempre retorne uma função de limpeza de useEffect que remove o ouvinte comsocket.off(). Isso não é negociável para o comportamento correto.
Autenticação
// Pass auth token when connecting
export const socket = io('http://localhost:3001', {
auth: {
token: localStorage.getItem('accessToken'),
},
autoConnect: false,
});
// Server verifies the token in a middleware before allowing the connection
// Update the token if it changes:
socket.auth.token = newToken;
socket.disconnect().connect(); // reconnect with new auth
Perguntas Frequentes
P: Por que estou recebendo mensagens duplicadas?
R: Você está adicionando ouvintes de eventos sem limpá-los. Cada nova renderização adiciona outro ouvinte. Sempre retorne uma função de limpeza de useEffect que chamasocket.off() para remover o ouvinte. Este é o erro mais comum do Socket.io + React.
P: Devo criar o soquete dentro ou fora do componente?
R: Fora — crie uma única instância de soquete compartilhado em um módulo separado (socket.js) e importe-o. Criá-lo dentro de um componente causa uma nova conexão em cada renderização. Uma instância compartilhada é o padrão correto.
P: Como faço para lidar com o soquete em vários componentes?
R: Importe a instância de soquete compartilhado em cada componente que precisa dele ou use um provedor de contexto para compartilhar o estado da conexão. Cada componente assina os eventos necessários (com limpeza) por meio de seu próprio useEffect. O único soquete lida com todos eles.
P: Socket.io ou WebSocket nativo?
R: Socket.io para aplicativos React de produção – adiciona reconexão automática, fallback para pesquisas longas, salas e uma API baseada em eventos que economiza um trabalho significativo. O WebSocket nativo é de nível inferior e exige que você mesmo crie a reconexão e o tratamento de eventos.
P: Como posso testar recursos em tempo real?
R: Abra o aplicativo em várias guias ou janelas do navegador para simular vários usuários e verificar a propagação de mensagens/eventos. Para testes automatizados, simule o soquete ou use um servidor Socket.io de teste. O teste manual de múltiplas guias é a maneira mais rápida de verificar se o comportamento em tempo real funciona.
Conclusão
Adicionar recursos em tempo real ao React com Socket.io é simples com os padrões certos:crie uma única instância de soquete compartilhado fora dos componentes, envolva a conexão e a manipulação de eventos em ganchos personalizados e SEMPRE limpe os ouvintes na função de retorno de useEffect. O padrão de gancho personalizado (useSocket, useMessages, useTyping) mantém sua lógica em tempo real organizada e reutilizável. Lide com a reconexão sincronizando novamente o estado após a recuperação da conexão e transmita tokens de autenticação na conexão para soquetes seguros. A regra mais importante é limpar os ouvintes comsocket.off() – esquecer que causa manipuladores duplicados e é o principal bug do Socket.io + React. Com esses padrões, você pode criar chat, notificações, painéis ao vivo e recursos colaborativos de forma limpa no React.
🔗 Share this article
✍️ Leave a Comment