Optimize React WebSocket Performance with Buffering
Job to be done: Optimize React WebSocket performance by buffering messages instead of rendering every message
🇳🇬 Ways to use this in Nigeria
Ideas to get you started, adapt to your situation.
- Student
Build a real-time chat app for your project group to share notes and updates faster without lag.
- 9-5 employee
Optimize a dashboard displaying live stock prices or sports scores to prevent UI slowdowns.
- Entrepreneur
Develop a faster, smoother live feed for your app's users to see real-time updates.
What you’ll get
You will learn how to prevent your React application from slowing down when receiving many messages very quickly over a WebSocket connection. Instead of updating your app’s display for every single message, you’ll learn to group them together and show them in batches. This approach is best for handling high-frequency data streams without making your user interface laggy.
Tools you need
- React (free): A JavaScript library for building user interfaces. You’ll use it to display the messages.
- WebSocket (free): A technology that allows for two-way communication between your browser and a server. This is how messages are sent.
- requestAnimationFrame (free): A browser function that tells the browser you want to perform an animation and requests that the browser call a specified function to update an animation before the next repaint. It helps sync updates with the screen’s refresh rate.
- useSyncExternalStore (free): A React hook that lets your component subscribe to an external data store. This is used to get the batched messages without causing performance issues.
- TanStack Query (free): A library for managing server state in React applications. It can be used to handle the buffered messages efficiently.
Steps
-
Create a message store: Set up a JavaScript file (e.g.,
messageStore.js) to hold the incoming messages in a temporary list (a buffer) and manage subscriptions. This store will not be directly observed by React.- You will need to define variables to hold the buffer, a list of functions that want to be notified when new messages arrive (subscribers), a flag to track if an update is already scheduled, and the current list of messages to display (snapshot).
- Create a function
pushMessagethat adds new messages to the buffer and schedules an update. - Create a function
scheduleFlushthat usesrequestAnimationFrameto process the buffer. InsiderequestAnimationFrame, it will take messages from the buffer, combine them with the existing snapshot, limit the total number of messages, clear the buffer, and then notify all subscribers. - Create a
subscribefunction that allows components to register their interest in new messages and agetSnapshotfunction that returns the current list of messages.
-
Create a React hook to consume messages: In a separate file (e.g.,
useMessages.js), create a custom hook that usesuseSyncExternalStoreto connect your React components to themessageStore.- This hook will call
subscribeto listen for updates andgetSnapshotto get the latest messages. - The hook will return the messages to your component.
- This hook will call
-
Connect the WebSocket to the message store: In your WebSocket handling file (e.g.,
socket.js), establish the WebSocket connection.- When a message arrives (
ws.onmessage), parse the message data and then call thepushMessagefunction from yourmessageStoreinstead of directly callingsetStatein your React component.
- When a message arrives (
-
Use the hook in your React component: In your main React component, import and use the
useMessageshook to get the list of messages.- Render the messages provided by the hook. Because the hook provides batched updates, your component will re-render much less often, improving performance.
-
Optional: Integrate with TanStack Query: If you are using TanStack Query, instead of directly feeding coalesced updates into
useSyncExternalStore, you can update the TanStack Query cache.- Modify the
scheduleFlushfunction inmessageStore.jsto usesetQueryDataor invalidation from TanStack Query to update its cache with the coalesced messages. This keeps TanStack Query as the authoritative source for your data.
- Modify the
Original source
This workflow is based on an article by Nainik Mehta posted on DEV Community. The author explains how to improve the performance of React applications that handle frequent updates from WebSockets by buffering messages and rendering them in batches.
Notes & variations
- Free-tier alternatives: All the tools mentioned are free to use for this purpose. You can use a simple array for the buffer and subscribers if you don’t want to use
useSyncExternalStoreor TanStack Query, but these provide more robust solutions. - Common mistake: Directly calling
setStatewithin thews.onmessagecallback is the most common performance pitfall. This leads to too many re-renders. Always buffer and batch updates. - Tip for better results: Using
requestAnimationFrameis generally preferred over a simplesetTimeoutfor flushing the buffer because it aligns updates with the browser’s rendering cycle, leading to smoother animations and less perceived lag.