home / skills / lobehub / lobe-chat / zustand

zustand skill

/.agents/skills/zustand

This skill guides Zustand state management for store code, actions, and slices, improving state reliability and developer productivity.

npx playbooks add skill lobehub/lobe-chat --skill zustand

Review the files below or copy the command above to add this skill to your agents.

Files (3)
SKILL.md
2.2 KB
---
name: zustand
description: Zustand state management guide. Use when working with store code (src/store/**), implementing actions, managing state, or creating slices. Triggers on Zustand store development, state management questions, or action implementation.
---

# LobeChat Zustand State Management

## Action Type Hierarchy

### 1. Public Actions
Main interfaces for UI components:
- Naming: Verb form (`createTopic`, `sendMessage`)
- Responsibilities: Parameter validation, flow orchestration

### 2. Internal Actions (`internal_*`)
Core business logic implementation:
- Naming: `internal_` prefix (`internal_createTopic`)
- Responsibilities: Optimistic updates, service calls, error handling
- Should not be called directly by UI

### 3. Dispatch Methods (`internal_dispatch*`)
State update handlers:
- Naming: `internal_dispatch` + entity (`internal_dispatchTopic`)
- Responsibilities: Calling reducers, updating store

## When to Use Reducer vs Simple `set`

**Use Reducer Pattern:**
- Managing object lists/maps (`messagesMap`, `topicMaps`)
- Optimistic updates
- Complex state transitions

**Use Simple `set`:**
- Toggling booleans
- Updating simple values
- Setting single state fields

## Optimistic Update Pattern

```typescript
internal_createTopic: async (params) => {
  const tmpId = Date.now().toString();

  // 1. Immediately update frontend (optimistic)
  get().internal_dispatchTopic(
    { type: 'addTopic', value: { ...params, id: tmpId } },
    'internal_createTopic'
  );

  // 2. Call backend service
  const topicId = await topicService.createTopic(params);

  // 3. Refresh for consistency
  await get().refreshTopic();
  return topicId;
},
```

**Delete operations**: Don't use optimistic updates (destructive, complex recovery)

## Naming Conventions

**Actions:**
- Public: `createTopic`, `sendMessage`
- Internal: `internal_createTopic`, `internal_updateMessageContent`
- Dispatch: `internal_dispatchTopic`
- Toggle: `internal_toggleMessageLoading`

**State:**
- ID arrays: `messageLoadingIds`, `topicEditingIds`
- Maps: `topicMaps`, `messagesMap`
- Active: `activeTopicId`
- Init flags: `topicsInit`

## Detailed Guides

- Action patterns: `references/action-patterns.md`
- Slice organization: `references/slice-organization.md`

Overview

This skill is a practical guide for implementing and maintaining Zustand stores in a TypeScript codebase. It focuses on clear action hierarchies, naming conventions, optimistic updates, and when to use reducers versus simple set operations. Use it to standardize state logic across store files and slices under src/store/**.

How this skill works

The guide defines three action layers: public actions for UI-facing flows, internal actions for core business logic and optimistic updates, and dispatch methods that perform reducers and direct state mutations. It explains patterns for optimistic creation, when to avoid optimistic deletes, and how to organize state as maps, ID arrays, and flags. Concrete naming rules and examples help you implement consistent, testable store code.

When to use it

  • When creating or updating Zustand stores and slices under src/store/**
  • When implementing UI-facing actions that require validation or orchestration
  • When writing internal business logic that needs optimistic updates or service calls
  • When deciding whether to use reducer patterns for lists/maps or simple set for primitives
  • When adding loading or editing state tracked by ID arrays or maps

Best practices

  • Split actions into public (UI), internal (business logic), and dispatch (state mutations)
  • Use reducer pattern for lists/maps and optimistic updates; use set for booleans and single fields
  • Prefix internal methods with internal_ and dispatch handlers with internal_dispatch for clarity
  • Avoid optimistic deletes; perform server-confirmed deletions and then refresh state
  • Track transient states with ID arrays (e.g., messageLoadingIds) and maps (e.g., messagesMap)

Example use cases

  • Implementing createTopic: perform optimistic add with tmpId, call backend, then refresh
  • Sending a message: public sendMessage validates input, internal action handles optimistic UI and error recovery
  • Toggling loading state for a message using internal_toggleMessageLoading and messageLoadingIds
  • Managing topic lists with topicMaps and topicsInit using reducer-style dispatches
  • Updating message content via internal_updateMessageContent with a reducer-driven update

FAQ

When should I prefer a reducer over a simple set call?

Use reducers for collections, maps, and complex transitions or optimistic flows. Use simple set for toggles or single-field updates.

Can I call internal_* actions from UI components?

No. Internal actions are meant for business logic and optimistic handling and should be invoked by public actions to keep validation and orchestration centralized.