Real-time State Sync
Part of the Full Stack Developer Roadmap
In short
A WebSocket event says 'card 9 moved to Done'. Now what? Merging that into your React state without duplicates, flicker or stale overwrites is the part most tutorials skip.
Keeping your frontend's displayed state correctly synchronized with real-time events from the backend — merging live updates into React Query's cache or local state without causing UI glitches.

Think of it like this
Calls at the mandi auction
At a mandi auction, the auctioneer calls out each new bid and everyone updates the price in their head. If you step out for chai, you don't guess when you return. You ask 'what's the price now?' and then follow the calls again. Live events update state; reconnecting means refetching first.
Watch it happen
One move, two tabs, one truth
card:move {id 9, Done}
1/6Asha drags card 9 to Done. Her UI moves it instantly and emits the change.
The big ideas
What you need to know
Merge, don't append
Update items by id. Pushing every event into an array creates duplicates when the same change arrives twice.
Versions beat timestamps
Send a version number with each change, and ignore events older than what you already have.
Missed events are normal
Connections drop. On reconnect, refetch the current state, then apply new events on top.
The server is the referee
Clients send intents. The server saves, decides the final value and broadcasts it, so every screen ends up the same.
See it in code
Walk through the code
1useEffect(() => {2 const onMoved = (card: Card) => {3 queryClient.setQueryData<Card[]>(["board", boardId], (old = []) =>4 old.map((c) => (c.id === card.id && card.version > c.version ? card : c)),5 );6 };7 const onReconnect = () => queryClient.invalidateQueries({ queryKey: ["board", boardId] });8 9 socket.on("card:moved", onMoved);10 socket.io.on("reconnect", onReconnect);11 return () => {12 socket.off("card:moved", onMoved);13 socket.io.off("reconnect", onReconnect);14 };15}, [boardId]);1/3Each event replaces the matching card, but only if its version is newer. Old or repeated events change nothing.
Socket.io client plus TanStack Query. The board's cached list is the single place real-time events land.
Save yourself the pain
Common mistakes
Oops
Appending every incoming event to state with [...old, item].
Do this instead
Upsert by id. The same event can arrive twice, especially around reconnects.
Oops
Forgetting socket.off in the effect cleanup.
Do this instead
Without cleanup, Strict Mode and page changes stack up listeners, and each event gets applied several times.
Oops
Trusting the cache after a reconnect.
Do this instead
Refetch once when the socket reconnects. Events sent while you were offline are gone for good.
Your mission
Two windows, one board
Quick check
Ravi's tab receives card 9 with version 11, but his cache already holds version 12. What should it do?
Go deeper
- 1Update a React Query cache directly when a real-time event arrives, instead of refetching everything.
- 2Handle out-of-order or duplicate real-time events gracefully.
- 3Test what happens to your UI when the real-time connection briefly drops and reconnects.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.