Socket.io Client - Frontend Implementation Guide
A comprehensive guide to implementing Socket.io client in React applications with JWT authentication and real-time communication features.
Table of Contents​
Prerequisites​
# Client dependencies
npm install socket.io-client
Client-Side Implementation​
Step 1: Create Socket Hook​
import { io } from 'socket.io-client';
import { useState, useEffect } from 'react';
const useSocket = (token) => {
const [socket, setSocket] = useState(null);
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
if (!token) return;
const socketInstance = io('http://localhost:3001', {
auth: { token }
});
socketInstance.on('connect', () => {
setIsConnected(true);
});
setSocket(socketInstance);
return () => socketInstance.disconnect();
}, [token]);
return { socket, isConnected };
};
Step 2: Use Socket in Components​
const ChatComponent = () => {
const token = 'your-jwt-token'; // Get from auth system
const { socket, isConnected } = useSocket(token);
useEffect(() => {
if (!socket) return;
socket.on('message', (data) => {
console.log('Received:', data);
});
return () => socket.off('message');
}, [socket]);
return (
<div>
<p>Status: {isConnected ? 'Connected' : 'Disconnected'}</p>
</div>
);
};
Best Practices​
-
Security
- Implement token expiration and renewal
- Use HTTPS in production
- Validate token payload
-
Error Handling
- Implement reconnection logic
- Handle connection errors gracefully
- Log security-related events
-
Performance
- Clean up event listeners
- Handle disconnections properly
Common Issues and Solutions​
-
Connection Issues
// Handle connection errors
socket.on('connect_error', (error) => {
console.error('Connection failed:', error.message);
// Implement retry logic
}); -
Token Expiration
// Check token expiration before emitting events
const isTokenValid = () => {
const token = socket.auth.token;
try {
jwt.verify(token, JWT_SECRET);
return true;
} catch {
return false;
}
};
Advanced Usage​
Custom Events​
// Client-side
socket.on('notification', (data) => {
showNotification(data.message);
});
// Emit events
socket.emit('sendMessage', {
message: 'Hello World',
room: 'general'
});
Room Management​
// Join a room
socket.emit('joinRoom', 'roomId');
// Listen for room events
socket.on('roomJoined', (roomId) => {
console.log(`Joined room: ${roomId}`);
});
socket.on('userJoined', (user) => {
console.log(`${user.name} joined the room`);
});
Reconnection Logic​
const useSocketWithReconnect = (token) => {
const [socket, setSocket] = useState(null);
const [isConnected, setIsConnected] = useState(false);
const [reconnectAttempts, setReconnectAttempts] = useState(0);
const maxReconnectAttempts = 5;
useEffect(() => {
if (!token) return;
const socketInstance = io('http://localhost:3001', {
auth: { token },
reconnection: true,
reconnectionAttempts: maxReconnectAttempts,
reconnectionDelay: 1000,
});
socketInstance.on('connect', () => {
setIsConnected(true);
setReconnectAttempts(0);
});
socketInstance.on('disconnect', () => {
setIsConnected(false);
});
socketInstance.on('reconnect_attempt', (attemptNumber) => {
setReconnectAttempts(attemptNumber);
if (attemptNumber > maxReconnectAttempts) {
console.error('Max reconnection attempts reached');
}
});
setSocket(socketInstance);
return () => socketInstance.disconnect();
}, [token]);
return { socket, isConnected, reconnectAttempts };
};
Complete Example​
import React, { useState, useEffect } from 'react';
import { useSocket } from './hooks/useSocket';
const ChatApp = () => {
const [messages, setMessages] = useState([]);
const [inputMessage, setInputMessage] = useState('');
const token = localStorage.getItem('authToken');
const { socket, isConnected } = useSocket(token);
useEffect(() => {
if (!socket) return;
// Listen for incoming messages
socket.on('message', (newMessage) => {
setMessages(prev => [...prev, newMessage]);
});
// Listen for user join/leave events
socket.on('userJoined', (user) => {
setMessages(prev => [...prev, {
type: 'system',
content: `${user.name} joined the chat`
}]);
});
socket.on('userLeft', (user) => {
setMessages(prev => [...prev, {
type: 'system',
content: `${user.name} left the chat`
}]);
});
return () => {
socket.off('message');
socket.off('userJoined');
socket.off('userLeft');
};
}, [socket]);
const sendMessage = () => {
if (!inputMessage.trim() || !socket) return;
socket.emit('sendMessage', {
content: inputMessage,
timestamp: new Date().toISOString()
});
setInputMessage('');
};
return (
<div className="chat-app">
<div className="connection-status">
Status: {isConnected ? '🟢 Connected' : '🔴 Disconnected'}
</div>
<div className="messages">
{messages.map((message, index) => (
<div key={index} className={`message ${message.type}`}>
{message.content}
</div>
))}
</div>
<div className="input-area">
<input
type="text"
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
placeholder="Type a message..."
disabled={!isConnected}
/>
<button onClick={sendMessage} disabled={!isConnected}>
Send
</button>
</div>
</div>
);
};
export default ChatApp;