Introduction
WebSocket connections are special communication channels that allow web browsers and servers to talk to each other in real-time. When you have multiple tabs or windows open in your browser, each one establishes its own WebSocket connection. This can be wasteful and affect the performance of your web application.
A better approach is to share a single WebSocket connection across all open tabs and windows. In this blog post, we’ll explore how to achieve this using BroadcastChannel and SharedWorker.
Why Share WebSocket Connections?
When multiple tabs create separate WebSocket connections, it can lead to:
- Increased server load : Multiple connections mean multiple active sockets on the server.
- Unnecessary network traffic : Each tab sends and receives duplicate data.
- Higher resource consumption : More connections mean higher CPU and memory usage.
By sharing a WebSocket connection, we reduce overhead, improve performance, and ensure a smoother user experience.
Frontend Setup in React
Let’s create a React app using the following command:
//Create a react app using the following command
npm create vite@latest my-websocket-app --template react
cd my-websocket-app
npm install
//Command for run the React project
npm run dev
Solution 1 : Using BroadcastChannel in React.js
The BroadcastChannel API allows communication between different tabs of the same origin. We can use it to create a single WebSocket connection in one tab and share data with others.
Implementation
1. Create a WebSocket Context in React
We will use React’s Context API to manage the WebSocket connection and share it across components.
// WebSocketContext.jsx
import React, { createContext, useEffect, useState } from "react";
export const WebSocketContext = createContext(null);
const channel = new BroadcastChannel("websocket_channel");
export const WebSocketProvider = ({ children }) => {
const [messages, setMessages] = useState([]);
let socket = null;
useEffect(() => {
function connectWebSocket() {
socket = new WebSocket("wss://your-websocket-server.com"); // Replace with actual WebSocket URL
socket.onopen = () => {
console.log("WebSocket connected");
channel.postMessage({ type: "connected" });
};
socket.onmessage = (event) => {
const message = event.data;
setMessages((prev) => [...prev, message]);
channel.postMessage({ type: "message", data: message });
};
socket.onclose = () => {
console.log("WebSocket closed, reconnecting...");
setTimeout(connectWebSocket, 3000);
};
}
connectWebSocket();
channel.onmessage = (event) => {
if (event.data.type === "message") {
setMessages((prev) => [...prev, event.data.data]);
}
};
}, [ ]);
return (
{children}
);
};
2. Use the WebSocket Context in a Component
We can now use the WebSocket context in any React component to access the shared WebSocket messages.
// ChatComponent.jsx
import React, { useContext } from "react";
import { WebSocketContext } from "./WebSocketContext";
const ChatComponent = () => {
const { messages } = useContext(WebSocketContext);
return (
Messages
{messages.map((msg, index) => (
- {msg}
))}
);
};
export default ChatComponent;
3. Wrap Your App with WebSocketProvider
To ensure all components can access the WebSocket, wrap your application with WebSocketProvider.
// App.jsx
import React from "react";
import { WebSocketProvider } from "./WebSocketContext";
import ChatComponent from "./ChatComponent";
function App() {
return (
);
}
export default App;
Solution 2 : Using SharedWorker in React.js
A SharedWorker runs in the background and can be used by multiple browser tabs to share a WebSocket connection.
Implementation
1. Create a SharedWorker script
// sharedWorker.js
self.onconnect = function (event) {
const port = event.ports[0];
let socket = new WebSocket("wss://your-websocket-server.com");
socket.onopen = () => {
console.log("WebSocket connected");
};
socket.onmessage = (event) => {
port.postMessage(event.data);
};
socket.onclose = () => {
console.log("WebSocket closed, reconnecting...");
setTimeout(() => {
socket = new WebSocket("wss://your-websocket-server.com");
}, 3000);
};
port.onmessage = (event) => {
if (socket.readyState === WebSocket.OPEN) {
socket.send(event.data);
}
};
};
2. Use the SharedWorker in a React Component
// useSharedWorker.jsx
import { useEffect, useState } from "react";
export function useSharedWorker() {
const [messages, setMessages] = useState([]);
const worker = new SharedWorker("sharedWorker.js");
worker.port.start();
useEffect(() => {
worker.port.onmessage = (event) => {
setMessages((prev) => [...prev, event.data]);
};
}, [ ]);
function sendMessage(message) {
worker.port.postMessage(message);
}
return { messages, sendMessage };
}
;
3. Use it in a React Component
// ChatComponentWithWorker.jsx
import React from "react";
import { useSharedWorker } from "./useSharedWorker";
const ChatComponentWithWorker = () => {
const { messages, sendMessage } = useSharedWorker();
return (
Messages
{messages.map((msg, index) => (
- {msg}
))}
);
};
export default ChatComponentWithWorker;
Conclusion
By using BroadcastChannel and SharedWorker in a React.js application, we can efficiently share a single WebSocket connection across multiple tabs. BroadcastChannel is simple and works well for message sharing, while SharedWorker provides a persistent background process for better control.
Choose the approach that best fits your use case and optimize WebSocket usage in your React applications.