Working with WebSockets
In this page:
Why WebSockets Instead of Repeated Fetching
fetch() and axios are request-response: the client always asks first. For real-time data (like a live chat or stock ticker), repeatedly polling with fetch() is wasteful and slow. A WebSocket keeps one persistent connection open, letting the server push new data the instant it's available.
Note: Reach for WebSockets specifically when the SERVER needs to initiate sending data, not just respond to requests.
Warning: WebSockets add real complexity (connection management, reconnection logic) — don't use them for data that updates rarely; simple fetching is fine there.
Example: Why WebSockets Instead of Repeated Fetching
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
const [status] = React.useState("WebSocket connection would open here");
return <p>{status}</p>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Setting Up and Cleaning Up the Connection
A WebSocket connection is created inside useEffect with 'new WebSocket(url)', and must be explicitly closed in the effect's cleanup function when the component unmounts, or the connection leaks and stays open unnecessarily.
Note: Store the WebSocket instance in a variable inside the effect, so the cleanup function can reference the same instance to close it.
Warning: Forgetting ws.close() in the cleanup function means every remount of this component opens yet another connection, without ever closing the old ones.
Example: Setting Up and Cleaning Up the Connection
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
const [connected, setConnected] = React.useState(false);
React.useEffect(() => {
// const ws = new WebSocket("wss://example.com/socket");
// ws.onopen = () => setConnected(true);
setConnected(true);
return () => { /* ws.close(); */ setConnected(false); };
}, []);
return <p>{connected ? "Connected" : "Disconnected"}</p>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Receiving Messages with onmessage
The WebSocket instance's onmessage handler fires every time the server pushes a new message, whether or not the client asked for it. Updating state inside this handler is how new data flows from the server into your React UI in real time.
Note: Server messages usually arrive as JSON strings — parse them with JSON.parse(event.data) before using the data.
Warning: Blindly trusting and rendering message content without validation can be a security risk if the message source isn't fully trusted.
Example: Receiving Messages with onmessage
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
const [messages, setMessages] = React.useState(["Connected..."]);
React.useEffect(() => {
// ws.onmessage = (event) => setMessages(m => [...m, event.data]);
setTimeout(() => setMessages(m => [...m, "New message from server"]), 500);
}, []);
return <ul>{messages.map((m, i) => <li key={i}>{m}</li>)}</ul>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
- Forgetting to close the WebSocket connection in useEffect's cleanup function, leaking open connections when the component unmounts.
- Not handling the connection's error/close events, leaving the UI stuck if the connection drops.
- Setting up a new WebSocket connection on every render instead of just once, by missing the dependency array.
- WebSocket is a built-in browser API for persistent, two-way connections with a server.
- A WebSocket connection is created and managed inside useEffect, closed again in its cleanup function.
- The onmessage event handler receives data pushed from the server at any time, not just in response to a request.
- Unlike fetch/axios, WebSockets let the server send data without the client asking first.
The WebSocket API is supported in all modern browsers; works with any React version via useEffect.
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: