Back to Skills

store-data-structures

LobeHub Zustand store data-shape patterns. Use when designing store state, list/detail splits, normalized maps, reducers, messagesMap, topicsMap, or choosing shared type sources.

81,197stars15,758forksUpdated 8/4/2026

Security Assessment

Safe(96/100)
Security Score96/100

About store-data-structures

store-data-structures is a code-convention reference documenting how LobeHub structures data inside Zustand stores. It addresses a common frontend state-management problem: choosing the right data shape for fast list rendering, caching multiple detail pages, and supporting ergonomic optimistic updates. The guidance is opinionated and pattern-focused rather than executable — a set of DO/DON'T rules with TypeScript examples.

The core patterns separate list state from detail state: simple arrays for lists (refreshed as a whole, no per-item mutation) and normalized maps (Record<string, Detail>) for details, which allow caching multiple detail pages, per-item loading states, and multi-page navigation without refetching. Types come from @lobechat/types (never @lobechat/database), with distinct Detail and List item types where the List type is a subset that excludes heavy fields rather than an extends of Detail. The skill shows an initialState slice pattern with list, detail-map, per-item loading, and mutation-state flags, and a reducer pattern using Immer's produce with a discriminated-union of set/update/delete actions plus internal_dispatch* slice methods that keep dispatch shapes out of the component layer and skip no-op sets to avoid re-renders.

It targets LobeHub contributors and, more broadly, React/Zustand developers designing store state for list/detail splits, normalized maps (messagesMap, topicsMap), reducers, and shared type sources. The skill is marked user-invocable: false, meaning it is intended as internal reference guidance consulted during development rather than a user-triggered command.

FAQ

When should I use a Map versus an Array?

Use a Map (Record<string, Detail>) with a reducer for detail data that needs multi-page caching, optimistic updates, and per-item loading states; use a simple array for list display that refreshes as a whole without per-item mutation.

Where should store types come from?

From @lobechat/types — never from @lobechat/database. Each entity gets a Detail type and a List item type, where the List type is a subset excluding heavy fields (not an extends of Detail, which would pull heavy fields back in).

Why use a reducer for the detail map?

A reducer built on Immer's produce gives immutable updates, a type-safe discriminated union of actions, testability as a pure function, and reuse across optimistic updates and server-data writes; internal_dispatch* methods keep dispatch shapes out of UI components.

Is this skill something a user invokes directly?

No. It is marked user-invocable: false and serves as internal code-convention reference guidance consulted while designing LobeHub store slices.

How are unnecessary re-renders avoided?

The internal_dispatch method compares the next map against the current one and skips the set call entirely when nothing changed.

All Files

3 files
references/reducer.md3.2 KB
View
SKILL.md8.8 KB
View
references/types.md2.5 KB
View

Install store-data-structures

Download and extract the skill files to your .claude/skills/ directory.

Quick Setup:

  1. Copy the skill folder to .claude/skills/
  2. Claude will automatically detect and use the skill

Repository

lobehub/lobehub