🛠️ 店舗データStructures
??プリケーションのデータを効率的に管理するため、Zustandストア
📺 まず動画で見る(YouTube)
▶ 【衝撃】最強のAIエージェント「Claude Code」の最新機能・使い方・プログラミングをAIで効率化する超実践術を解説! ↗
※ jpskill.com 編集部が参考用に選んだ動画です。動画の内容と Skill の挙動は厳密には一致しないことがあります。
📜 元の英語説明(参考)
Zustand store data structure patterns for LobeHub. Covers List vs Detail data structures, Map + Reducer patterns, type definitions, and when to use each pattern. Use when designing store state, choosing data structures, or implementing list/detail pages.
🇯🇵 日本人クリエイター向け解説
??プリケーションのデータを効率的に管理するため、Zustandストア
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-17
- 取得日時
- 2026-05-17
- 同梱ファイル
- 3
💬 こう話しかけるだけ — サンプルプロンプト
- › Store Data Structures を使って、最小構成のサンプルコードを示して
- › Store Data Structures の主な使い方と注意点を教えて
- › Store Data Structures を既存プロジェクトに組み込む方法を教えて
これをClaude Code に貼るだけで、このSkillが自動発動します。
📖 Claude が読む原文 SKILL.md(中身を展開)
この本文は AI(Claude)が読むための原文(英語または中国語)です。日本語訳は順次追加中。
LobeHub Store Data Structures
How to structure data in Zustand stores for fast list rendering, multi-detail caching, and ergonomic optimistic updates.
Core Principles
✅ DO
- Separate List and Detail — different structures for list pages and detail pages
- Use Map for Details — cache multiple detail pages with
Record<string, Detail> - Use Array for Lists — simple arrays for list display
- Types from
@lobechat/types— never use@lobechat/databasetypes in stores - Distinguish List and Detail types — List types may have computed UI fields
❌ DON'T
- Don't use a single detail object — can't cache multiple pages
- Don't mix List and Detail types — they have different purposes
- Don't use database types — use types from
@lobechat/types - Don't use Map for lists — simple arrays are sufficient
Type Definitions
Each entity gets its own file under @lobechat/types/. Each file exports two types:
- Detail type — full entity, including heavy fields (rubrics, content, editor state, …)
- List item type — a subset that excludes heavy fields, may add computed UI fields (counts, timestamps formatted for display)
Important: the List type is a subset, not an extends of Detail. Extending pulls the heavy fields right back in.
See
references/types.mdfor full worked examples (Benchmark, Document) and the heavy-field exclusion checklist.
When to Use Map vs Array
Use Map + Reducer — for Detail Data
✅ Detail page data caching — multiple detail pages cached simultaneously ✅ Optimistic updates — update UI before API responds ✅ Per-item loading states — track which items are being updated ✅ Multi-page navigation — user can switch between details without refetching
benchmarkDetailMap: Record<string, AgentEvalBenchmark>;
Examples: benchmark detail pages, dataset detail pages, user profiles.
Use Simple Array — for List Data
✅ List display — lists, tables, cards ✅ Refresh as a whole — entire list refreshes together ✅ No per-item updates — no need to mutate individual rows in place ✅ Simple data flow — fewer moving parts
benchmarkList: AgentEvalBenchmarkListItem[];
Examples: benchmark list, dataset list, user list.
State Structure Pattern
// src/store/eval/slices/benchmark/initialState.ts
import type { AgentEvalBenchmark, AgentEvalBenchmarkListItem } from '@lobechat/types';
export interface BenchmarkSliceState {
// List — simple array
benchmarkList: AgentEvalBenchmarkListItem[];
benchmarkListInit: boolean;
// Detail — map for multi-entity caching
benchmarkDetailMap: Record<string, AgentEvalBenchmark>;
loadingBenchmarkDetailIds: string[]; // per-item loading
// Mutation states (drive form-level UI)
isCreatingBenchmark: boolean;
isUpdatingBenchmark: boolean;
isDeletingBenchmark: boolean;
}
export const benchmarkInitialState: BenchmarkSliceState = {
benchmarkList: [],
benchmarkListInit: false,
benchmarkDetailMap: {},
loadingBenchmarkDetailIds: [],
isCreatingBenchmark: false,
isUpdatingBenchmark: false,
isDeletingBenchmark: false,
};
Reducer Pattern (for Detail Map)
When the Detail Map needs optimistic updates (i.e. the user edits a row and the UI should reflect it before the server confirms), wire a typed reducer instead of inlining set calls. This keeps mutations testable and the dispatch surface small.
See
references/reducer.mdfor the full discriminated-union action types, theproduce-based reducer, and theinternal_dispatch*slice methods that connect them to Zustand.
Data Structure Comparison
❌ WRONG — Single Detail Object
interface BenchmarkSliceState {
benchmarkDetail: AgentEvalBenchmark | null;
isLoadingBenchmarkDetail: boolean;
}
Problems:
- Can only cache one detail page at a time
- Switching between details forces refetch
- No optimistic updates
- No per-item loading states
✅ CORRECT — Separate List and Detail
interface BenchmarkSliceState {
benchmarkList: AgentEvalBenchmarkListItem[];
benchmarkListInit: boolean;
benchmarkDetailMap: Record<string, AgentEvalBenchmark>;
loadingBenchmarkDetailIds: string[];
isCreatingBenchmark: boolean;
isUpdatingBenchmark: boolean;
isDeletingBenchmark: boolean;
}
Benefits:
- Cache multiple detail pages
- Fast navigation between cached details
- Optimistic updates via reducer
- Per-item loading states
- Clear separation of concerns
Component Usage
Accessing List Data
const BenchmarkList = () => {
const benchmarks = useEvalStore((s) => s.benchmarkList);
const isInit = useEvalStore((s) => s.benchmarkListInit);
if (!isInit) return <Loading />;
return (
<div>
{benchmarks.map((b) => (
<BenchmarkCard key={b.id} name={b.name} testCaseCount={b.testCaseCount} />
))}
</div>
);
};
Accessing Detail Data
const BenchmarkDetail = () => {
const { benchmarkId } = useParams<{ benchmarkId: string }>();
const benchmark = useEvalStore((s) =>
benchmarkId ? s.benchmarkDetailMap[benchmarkId] : undefined,
);
const isLoading = useEvalStore((s) =>
benchmarkId ? s.loadingBenchmarkDetailIds.includes(benchmarkId) : false,
);
if (!benchmark) return <Loading />;
return (
<div>
<h1>{benchmark.name}</h1>
{isLoading && <Spinner />}
</div>
);
};
Using Selectors (Recommended)
// src/store/eval/slices/benchmark/selectors.ts
export const benchmarkSelectors = {
getBenchmarkDetail: (id: string) => (s: EvalStore) => s.benchmarkDetailMap[id],
isLoadingBenchmarkDetail: (id: string) => (s: EvalStore) =>
s.loadingBenchmarkDetailIds.includes(id),
};
// In component
const benchmark = useEvalStore(benchmarkSelectors.getBenchmarkDetail(benchmarkId!));
const isLoading = useEvalStore(benchmarkSelectors.isLoadingBenchmarkDetail(benchmarkId!));
Decision Tree
Need to store data?
│
├─ Is it a LIST for display?
│ └─ ✅ Use simple array: `xxxList: XxxListItem[]`
│ - May include computed fields
│ - Refreshed as a whole
│ - No optimistic updates needed
│
└─ Is it DETAIL page data?
└─ ✅ Use Map: `xxxDetailMap: Record<string, Xxx>`
- Cache multiple details
- Support optimistic updates
- Per-item loading states
- Requires reducer for mutations
Checklist
When designing store state structure:
- [ ] Organize types by entity in separate files (e.g.
benchmark.ts,agentEvalDataset.ts) - [ ] Create Detail type (full entity with all fields including heavy ones)
- [ ] Create ListItem type:
- [ ] Subset of Detail (exclude heavy fields)
- [ ] May include computed statistics for UI
- [ ] NOT
extendsDetail
- [ ] Use array for list data:
xxxList: XxxListItem[] - [ ] Use Map for detail data:
xxxDetailMap: Record<string, Xxx> - [ ] Per-item loading:
loadingXxxDetailIds: string[] - [ ] Reducer for detail map if optimistic updates needed (see
references/reducer.md) - [ ] Internal dispatch and loading methods
- [ ] Selectors for clean access (optional but recommended)
- [ ] Document in comments which fields are excluded from List and why
Best Practices
- File organization — one entity per file, not mixed
- List is a subset — ListItem excludes heavy fields, does not
extendsDetail - Clear naming —
xxxListfor arrays,xxxDetailMapfor maps - Consistent patterns — all detail maps follow the same shape
- Type safety — never use
any, always use proper types - Document exclusions — comment which fields are excluded and why
- Selectors — encapsulate access patterns
- Loading states — per-item for details, global for mutations
- Immutability — use Immer in reducers
Common Mistakes to Avoid
❌ DON'T extend Detail in List:
// Wrong — pulls heavy fields back in
export interface BenchmarkListItem extends Benchmark {
testCaseCount?: number;
}
✅ DO create separate subset:
export interface BenchmarkListItem {
id: string;
name: string;
// ... only necessary fields
testCaseCount?: number; // Computed
}
❌ DON'T mix entities in one file:
// Wrong — all entities in agentEvalEntities.ts
✅ DO separate by entity:
// Correct — separate files
// benchmark.ts
// agentEvalDataset.ts
// agentEvalRun.ts
Related Skills
data-fetching— how to fetch and update this datazustand— general Zustand patterns
同梱ファイル
※ ZIPに含まれるファイル一覧。`SKILL.md` 本体に加え、参考資料・サンプル・スクリプトが入っている場合があります。
- 📄 SKILL.md (9,210 bytes)
- 📎 references/reducer.md (3,307 bytes)
- 📎 references/types.md (2,577 bytes)