DEV Community

Cover image for How I Solved WebSocket "Event Drift" in React with a Custom NPM Package
Pankaj Kumar
Pankaj Kumar

Posted on

How I Solved WebSocket "Event Drift" in React with a Custom NPM Package

The "Scattered State" Nightmare
We’ve all been there. You’re building a real-time app—maybe a delivery tracker like my project UniMart or a collaborative dashboard. You start with one socket.on listener in a useEffect. Then another. Then another.

Before you know it, your WebSocket logic is scattered across five different components. This leads to the three Horsemen of Real-Time Bugs:

  • Memory Leaks: You forgot to call .off() in a cleanup function, and now you have 50 ghost listeners eating RAM.
  • Event Drift: Two components receive the same "location-update" at slightly different times, causing your UI to flicker or show inconsistent data.
  • Black-Box Debugging: You have no idea how a specific incoming packet changed your global state.

The Solution: Treating Sockets as a State Dispatcher
I decided to stop fighting useEffect and started treating my Socket instance like a Redux Store.

I built react-socket-sync, a lightweight, type-safe hook that centralizes all your socket events into a single Reducer.

🧠 The Architecture
Instead of scattered listeners, I designed a bridge that maps incoming events to specific state transitions.

TypeScript
// Define your "Brain" (The Reducer)

const reducers = {
  'driver-location': (state, pos) => ({ ...state, location: pos }),
  'order-status': (state, status) => ({ ...state, status }),
};
Enter fullscreen mode Exit fullscreen mode

// Use the hook to sync everything

const state = useSocketState(socket, reducers, initialState, { debug: true });
Enter fullscreen mode Exit fullscreen mode

Engineering Challenges
Building this wasn't just about the hook; it was about the NPM Ecosystem.

  1. The Hybrid Module Headache (ESM vs. CJS)
    Modern apps use ESM (Next.js/Vite), but many legacy tools still rely on CommonJS. I had to configure my build pipeline to output both, ensuring the library works everywhere without crashing the bundler.

  2. X-Ray Observability
    I built an internal Logger that provides color-coded console output when debug: true is passed. It tracks:

  • The Incoming Event
  • The Raw Payload
  • The Resulting State Change

This turned "invisible bugs" into a clear, readable timeline in the browser console.

Fighting the Build Cache
During development, I hit a massive roadblock with Next.js Turbopack caching local links. I had to master the npm pack workflow—creating local tarballs to simulate a real production install—before finally shipping to the registry.

Takeaways
Building an open-source library taught me more about Software Supply Chain Security (2FA) and API Design than any tutorial ever could.

If you're building real-time apps, check out the package! I'd love to hear your feedback on the architecture.

NPM
GitHub

Top comments (0)