clean-react-components
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseClean Components
整洁组件
React components are functions with UI responsibilities. Keep each component focused, make props readable at the call site, and use composition when variants start multiplying.
React组件是承担UI职责的函数。保持每个组件专注单一职责,让调用处的props具备可读性,当变体开始增多时使用组合方式。
R1: Component Boundaries
R1:组件边界
A component should usually own one of these jobs:
- A reusable primitive, like or
ButtonTextField - A domain display, like
InvoiceSummary - A workflow section, like
ShippingAddressStep - A route or screen that coordinates smaller components
If a component fetches data, transforms it, manages several UI modes, and renders a large tree, split coordination from presentation.
一个组件通常应承担以下职责之一:
- 可复用基础组件,如或
ButtonTextField - 领域展示组件,如
InvoiceSummary - 流程环节组件,如
ShippingAddressStep - 协调小型组件的路由或页面组件
如果一个组件既要获取数据、转换数据、管理多种UI模式,还要渲染大型组件树,那么请将协调逻辑与展示逻辑拆分。
R2, R12: Props
R2、R12:Props
tsx
// Bad - call site meaning is unclear
<UserCard user={user} true false "compact" />
// Good - one named concept
<UserCard user={user} variant="compact" showActions={false} />- Use object props for components; positional arguments do not apply to JSX.
- Keep prop names domain-specific.
- Avoid passing large objects when the component needs only a few fields, unless the object itself is the domain concept.
- Avoid boolean prop combinations that create unrelated modes.
When modes are mutually exclusive, model props as a discriminated union so invalid combinations cannot be rendered.
tsx
// Bad - caller can pass contradictory props
type BannerProps = {
isLoading?: boolean;
error?: Error;
message?: string;
};
// Good - each mode owns the props it needs
type BannerProps =
| { status: "loading" }
| { status: "error"; error: Error }
| { status: "ready"; message: string };tsx
// Bad - call site meaning is unclear
<UserCard user={user} true false "compact" />
// Good - one named concept
<UserCard user={user} variant="compact" showActions={false} />- 使用对象式props定义组件;位置参数不适用于JSX。
- 保持prop名称与领域相关。
- 除非对象本身是领域概念,否则当组件仅需要少数字段时,避免传递大对象。
- 避免使用布尔值prop组合来创建不相关的模式。
当模式互斥时,将props建模为区分联合类型,这样就不会渲染无效的组合。
tsx
// Bad - caller can pass contradictory props
type BannerProps = {
isLoading?: boolean;
error?: Error;
message?: string;
};
// Good - each mode owns the props it needs
type BannerProps =
| { status: "loading" }
| { status: "error"; error: Error }
| { status: "ready"; message: string };Composition Over Flags
组合优于标记
tsx
// Bad - one component owns unrelated modes
<Panel isModal isDismissible hasFooter />
// Good - structure communicates behavior
<Modal onDismiss={closeModal}>
<PanelFooter actions={actions} />
</Modal>Boolean props are fine for simple visual toggles. They are a smell when combinations create different components hiding behind one name.
tsx
// Bad - one component owns unrelated modes
<Panel isModal isDismissible hasFooter />
// Good - structure communicates behavior
<Modal onDismiss={closeModal}>
<PanelFooter actions={actions} />
</Modal>布尔值prop适用于简单的视觉切换。但当组合在一个名称下隐藏不同组件时,就需要警惕了。
R13: Conditional Rendering
R13:条件渲染
- Prefer guard clauses for empty, loading, and error states.
- Keep nested ternaries out of JSX.
- Extract named render sections only when the name adds meaning.
- Render explicit loading, error, empty, and success states when remote or nullable data can be in those states.
tsx
if (status === "loading") {
return <Spinner />;
}
if (status === "error") {
return <ErrorMessage error={error} />;
}
return <OrderDetails order={order} />;- 对于空状态、加载状态和错误状态,优先使用守卫语句。
- 避免在JSX中使用嵌套三元表达式。
- 仅当名称能增加含义时,才提取命名的渲染片段。
- 当远程或可空数据可能处于加载、错误、空或成功状态时,要显式渲染这些状态。
tsx
if (status === "loading") {
return <Spinner />;
}
if (status === "error") {
return <ErrorMessage error={error} />;
}
return <OrderDetails order={order} />;R14: Render Purity
R14:渲染纯度
Rendering should only calculate UI. Do not mutate data, write storage, start timers, subscribe, navigate, log analytics, or trigger network work during render. Put effects in event handlers, , data-fetching boundaries, or framework loaders/actions.
useEffect渲染应仅用于计算UI。不要在渲染期间修改数据、写入存储、启动定时器、订阅、导航、记录分析或触发网络请求。将副作用放在事件处理函数、、数据获取边界或框架加载器/动作中。
useEffectR9: Event Handlers
R9:事件处理函数
Event handlers should name the user action, not the element or mechanism. Keep the mutation path obvious at the call site.
tsx
// Bad - names the element, not the intent
function handleButtonClick() {
setItems(items.filter((item) => item.id !== id));
}
// Good - names what the user did
function handleRemoveItem(id: string) {
setItems((items) => items.filter((item) => item.id !== id));
}Avoid embedding complex logic or multiple state updates directly in JSX props. Extract a named handler when the intent would not be clear inline.
tsx
// Bad - mutation path is buried in JSX
<button onClick={() => {
setLoading(true);
setError(null);
submitOrder(cart);
}}>
Place Order
</button>
// Good - intent is named, path is visible
<button onClick={handlePlaceOrder}>Place Order</button>事件处理函数应命名用户的操作,而非元素或机制。在调用处保持修改路径清晰。
tsx
// Bad - names the element, not the intent
function handleButtonClick() {
setItems(items.filter((item) => item.id !== id));
}
// Good - names what the user did
function handleRemoveItem(id: string) {
setItems((items) => items.filter((item) => item.id !== id));
}避免在JSX props中嵌入复杂逻辑或多个状态更新。当内联写法无法清晰表达意图时,提取命名的处理函数。
tsx
// Bad - mutation path is buried in JSX
<button onClick={() => {
setLoading(true);
setError(null);
submitOrder(cart);
}}>
Place Order
</button>
// Good - intent is named, path is visible
<button onClick={handlePlaceOrder}>Place Order</button>Common Mistakes
常见错误
- Creating a dumping ground with no domain ownership.
components/ - Extracting one-line components that hide simple JSX.
- Passing through every layer instead of creating clear layout boundaries.
className - Memoizing components before measuring a real render problem.
- Mutating arrays or objects while preparing JSX.
- Calling navigation, storage, analytics, or request code during render.
- 创建无领域归属的目录,成为组件的“垃圾场”。
components/ - 提取仅一行代码的组件,隐藏了简单的JSX。
- 层层传递,而非创建清晰的布局边界。
className - 在未测量实际渲染问题前就对组件进行 memoize 处理。
- 在准备JSX时修改数组或对象。
- 在渲染期间调用导航、存储、分析或请求代码。