102 lines
4.1 KiB
Markdown
102 lines
4.1 KiB
Markdown
# State Management
|
|
|
|
> This document outlines the strategy and standards for managing state within this project, ensuring data flows predictably and efficiently.
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
We follow a tiered state management strategy. Decisions on where to store state are based on the **scope of the data** and the **nesting depth** of the components that require it.
|
|
|
|
---
|
|
|
|
## State Categories & Selection Criteria
|
|
|
|
### 1. Local State (`useState`)
|
|
- **When to use**: When the state is only used within a single component or passed down to immediate children via props.
|
|
- **Scope**: Private to the component or its direct sub-tree.
|
|
|
|
### 2. Module/Scoped State (`Context API`)
|
|
- **When to use**: When state needs to be accessed by deeply nested components (e.g., Grandparent to Grandchild) within a **single functional module**, but is not needed globally.
|
|
- **Scope**: Specific to a feature folder or a logical module.
|
|
|
|
### 3. Global State (`Zustand`)
|
|
- **When to use**: When data needs to be shared across **multiple independent modules** or throughout the entire application (e.g., User Auth, Theme, Global Settings).
|
|
- **Scope**: Application-wide.
|
|
|
|
---
|
|
|
|
## Persistence Strategy
|
|
|
|
To ensure a seamless user experience, we distinguish between data that should survive a page refresh and data that is volatile.
|
|
|
|
### 1. Persistent State (LocalStorage)
|
|
By default, global state that represents user progress or configuration should be synchronized with **LocalStorage**.
|
|
|
|
### 2. Volatile State (`temp.ts`)
|
|
For data that **should not be cached** (e.g., temporary UI toggles, scroll positions, transient navigation info), we use a dedicated store.
|
|
- **Action**: Create a `temp.ts` file in the store directory.
|
|
- **Usage**: All non-persistent data must reside here to avoid cluttering the browser's storage.
|
|
|
|
---
|
|
|
|
## Zustand Implementation Standards
|
|
|
|
When creating Zustand stores, follow the naming conventions for state and actions as shown below.
|
|
|
|
### Standard Pattern Example
|
|
```typescript
|
|
/**
|
|
* 临时状态存储接口,用于存放不需要持久化的数据
|
|
*/
|
|
interface TempStore {
|
|
/** 页面滚动条距离顶部的距离 */
|
|
scrollTop: number;
|
|
/** 当前路由的元信息(标题等) */
|
|
routeInfo: routeInfo;
|
|
/** 当前设备是否为移动端 */
|
|
mobile: boolean;
|
|
|
|
/** 修改滚动条位置 */
|
|
changeScroll: (value: number) => void;
|
|
/** 更新路由元信息 */
|
|
changeRouterInfo: (value: routeInfo) => void;
|
|
/** 设置移动端状态 */
|
|
setMobile: (value: boolean) => void;
|
|
}
|
|
|
|
const useTempStore = create<TempStore>((set) => ({
|
|
// 初始状态
|
|
scrollTop: 0,
|
|
routeInfo: {
|
|
title: '首页'
|
|
},
|
|
mobile: false,
|
|
|
|
// 方法实现
|
|
changeScroll: (value) => {
|
|
set(() => ({ scrollTop: value }));
|
|
},
|
|
changeRouterInfo: (value) => {
|
|
set(() => ({ routeInfo: value }));
|
|
},
|
|
setMobile: (data) => {
|
|
set(() => ({ mobile: data }));
|
|
}
|
|
}));
|
|
|
|
```
|
|
## Best Practices
|
|
- Derived State: Whenever possible, calculate values on-the-fly rather than storing them in the state (e.g., don't store isLoggedIn, store the token and derive isLoggedIn = !!token).
|
|
- Action Naming: Use clear verbs for actions:
|
|
- changeX: For updating numerical or incremental values.
|
|
- setX: For overwriting values or booleans.
|
|
- Selectors: Always use selectors when consuming Zustand stores to prevent unnecessary re-renders:
|
|
const scrollTop = useTempStore((state) => state.scrollTop);
|
|
- Context Cleanup: When using Context, ensure the Provider is wrapped at the lowest common ancestor to minimize the re-render scope.
|
|
- **Mandatory Documentation**: Every store interface and every single state/action property MUST have a JSDoc `/** */` comment written in **Chinese**.
|
|
## Common Mistakes
|
|
- **Missing Comments**: Defining a store or property without a `/** */` JSDoc comment.
|
|
- **Wrong Language**: Writing comments in English. **All code comments must be in Chinese.**
|
|
- **Globalizing everything**: Don't put state in Zustand just because it's "easier." If it's local to a form, use `useState`.
|
|
- **Direct State Mutation**: Never try to mutate the state directly. Always use the `set` function provided by the store. |