clean-react-state
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseClean State
整洁状态管理
State should have one owner and one source of truth. Store the minimal facts that change over time; derive everything else.
状态应只有一个所有者和单一数据源。仅存储随时间变化的最小必要信息;其余所有信息均通过派生得到。
R6: Local First
R6: 本地优先
Keep state close to where it is used. Lift state only when multiple components need to read or change it.
tsx
// Bad - parent owns state used by one child
function Page() {
const [isOpen, setIsOpen] = useState(false);
return <DetailsPanel isOpen={isOpen} onOpenChange={setIsOpen} />;
}
// Good - child owns local UI state
function DetailsPanel() {
const [isOpen, setIsOpen] = useState(false);
// ...
}将状态放在离使用它最近的位置。仅当多个组件需要读取或修改该状态时,才提升状态到父组件。
tsx
// 不良示例 - 父组件持有仅单个子组件使用的状态
function Page() {
const [isOpen, setIsOpen] = useState(false);
return <DetailsPanel isOpen={isOpen} onOpenChange={setIsOpen} />;
}
// 良好示例 - 子组件持有本地UI状态
function DetailsPanel() {
const [isOpen, setIsOpen] = useState(false);
// ...
}R5: Derived State
R5: 派生状态
Do not store values that can be calculated from props, state, or query data during render.
tsx
const completedItems = items.filter((item) => item.completed);
const hasCompletedItems = completedItems.length > 0;Use only when the computation is expensive or referential stability matters.
useMemo不要存储可在渲染时通过props、state或查询数据计算得到的值。
tsx
const completedItems = items.filter((item) => item.completed);
const hasCompletedItems = completedItems.length > 0;仅当计算成本高昂或引用稳定性很重要时,才使用。
useMemoReducers
Reducers
Use when state transitions are related and event-like. Keep actions domain-specific.
useReducertsx
type CartAction =
| { type: "item-added"; item: CartItem }
| { type: "item-removed"; itemId: string }
| { type: "discount-applied"; code: string };Avoid reducers that simply proxy with generic actions like .
setState{ type: "set-value" }当状态转换具有关联性且类似事件时,使用。保持操作与业务领域相关。
useReducertsx
type CartAction =
| { type: "item-added"; item: CartItem }
| { type: "item-removed"; itemId: string }
| { type: "discount-applied"; code: string };避免使用仅通过这类通用操作代理的reducers。
{ type: "set-value" }setStateContext
Context
- Use context for values many descendants need.
- Split read-heavy state from write actions when it prevents unnecessary rerenders.
- Do not use context as a default replacement for props.
- 当多个后代组件需要某个值时使用Context。
- 当可以避免不必要的重渲染时,将只读状态与写入操作分离。
- 不要将Context作为props的默认替代方案。
R7: Server State
R7: 服务端状态
Server data, cache lifetime, background refresh, optimistic updates, and request status are not ordinary local UI state. Prefer the project's data-fetching library when one exists.
Components that render server state should account for every user-visible state the data source can produce: loading, error, empty, and success. Do not collapse missing data, failed data, and still-loading data into the same fallback unless the product behavior intentionally treats them the same.
服务端数据、缓存生命周期、后台刷新、乐观更新和请求状态不属于普通的本地UI状态。如果项目已有数据获取库,优先使用该库。
渲染服务端状态的组件应考虑数据源可能产生的所有用户可见状态:加载中、错误、空数据和成功。除非产品行为有意将它们视为相同情况,否则不要将数据缺失、请求失败和仍在加载的情况合并为同一个回退状态。
Common Mistakes
常见错误
- Mirroring props into state without a reset strategy.
- Keeping both and
selectedIdin state.selectedItem - Using global stores for state that belongs to one screen.
- Mixing remote cache state and transient UI state in one object.
- Rendering nullable server data as if loading, error, and empty states were equivalent.
- 在没有重置策略的情况下将props镜像到state中。
- 同时在state中保存和
selectedId。selectedItem - 为仅属于单个页面的状态使用全局存储。
- 将远程缓存状态和临时UI状态混合在一个对象中。
- 将可空的服务端数据视为加载中、错误和空状态等价的情况进行渲染。