Stop Using Arrays for Real-Time State in React
The Problem with Arrays
For years, I stored fetched data in arrays—whether using React state, React Query, or SWR. While fetching libraries like TanStack Query are my go-to, let’s look at a simple useState example. In applications using WebSockets or Server-Sent Events (SSE), we often need to update this state in real-time based on incoming events.
Using an Array:
// react component
function ListComponent() {
const [items, setItems] = useState([])
useEffect(() => {
// Initial fetch
socket
.service('items')
.find()
.then(({ data }) => setItems(data))
}, [])
useEffect(() => {
// Listeners
socket.service('items').on('created', (item) => {
setItems([...items, item])
})
socket.service('items').on('patched', (item) => {
setItems(items.map((t) => (t.id === item.id ? item : t)))
})
socket.service('items').on('removed', (item) => {
setItems(items.filter((t) => t.id !== item.id))
})
})
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
)
}
This works, but mapping over the entire array for every patch or filter for every removal feels inefficient and verbose.
Using a Map Instead
ES6 introduced the Map object (see MDN Docs), which holds key-value pairs. Using the id as the key simplifies updates significantly.
Using a Map:
// react component
function ListComponent() {
const [items, setItems] = useState(new Map())
useEffect(() => {
socket
.service('items')
.find()
.then(({ data }) => {
// Create Map from array: [id, item]
setItems(new Map(data.map((d) => [d.id, d])))
})
}, [])
useEffect(() => {
;['created', 'patched', 'removed'].forEach((event) => {
socket.service('items').on(event, (item) => {
if (event === 'removed') {
items.delete(item.id)
} else {
items.set(item.id, item)
}
// Trigger re-render with new Map reference
setItems(new Map(items))
})
})
})
return (
<ul>
{Array.from(items).map(([id, item]) => (
<li key={id}>{item.name}</li>
))}
</ul>
)
}
The key is creating a new Map reference (new Map(items)) to trigger the state update. This approach simplifies the logic—no more mapping or filtering the entire collection just to update one item.
If you have feedback, reach out on Twitter.
Building Compass: A Life Assistant That Still Works When the AI Does Not
I wanted a more customizable workout tracker, a budget my wife and I could share, and AI help on both. So I built Compass on TanStack Start and Cloudflare, and made sure it still works when the AI does not.
Surveyor: A Field-Tested Network Discovery Tool for MSPs
From Raspberry Pi prototype to Wails desktop app: how Surveyor replaces manual nmap commands with stealth scanning and RFC 1918 auto-discovery.
TanStack Hono: A Lightweight SSR Monolith Without the Meta-Framework Weight
Single-process SSR + CSR with type-safe RPC, Vite, React, and Hono. The goal is low operational complexity without giving up dynamic UI.