← Back to React Course | Chapter 10: Data Fetching & Async | Lesson 8 of 10

Working with WebSockets

A WebSocket is like an open phone line between your app and the server, so either side can talk anytime, instead of one side having to call and hang up for every single message.

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

markup
<!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

markup
<!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

markup
<!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>
Common Mistakes
  1. Forgetting to close the WebSocket connection in useEffect's cleanup function, leaking open connections when the component unmounts.
  2. Not handling the connection's error/close events, leaving the UI stuck if the connection drops.
  3. Setting up a new WebSocket connection on every render instead of just once, by missing the dependency array.
Chapter Summary
  • 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.
Browser Support

The WebSocket API is supported in all modern browsers; works with any React version via useEffect.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.