ideal-react-component

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Ideal React Component Structure

理想的React组件结构

Overview

概述

A battle-tested pattern for organizing React component files that emphasizes readability, maintainability, and logical flow. This structure helps teams maintain consistency and makes components easier to understand at a glance.
Core principle: Declare everything in a predictable order--imports to styles to types to logic to render--so developers know where to find things.
这是一套经过实战检验的React组件文件组织模式,强调可读性、可维护性和逻辑连贯性。该结构有助于团队保持一致性,让组件内容一目了然。
核心原则: 按照可预测的顺序声明所有内容——从导入到样式、类型定义、逻辑再到渲染,让开发者能快速定位所需内容。

When to Use

适用场景

Always use when:
  • Creating new React components
  • Refactoring existing components
  • Reviewing component structure during code review
  • Onboarding developers to component patterns
Useful for:
  • Establishing team conventions
  • Maintaining large component libraries
  • Teaching React best practices
  • Reducing cognitive load when reading components
Avoid when:
  • Working with class components (this pattern is for function components)
  • Component is < 20 lines and simple (don't over-engineer)
  • Project has different established conventions (consistency > perfection)
始终适用场景:
  • 创建新的React组件
  • 重构现有组件
  • 代码评审中检查组件结构
  • 指导新开发者熟悉组件模式
适用优势:
  • 建立团队统一规范
  • 维护大型组件库
  • 教授React最佳实践
  • 降低阅读组件时的认知负担
避免使用场景:
  • 处理类组件(该模式针对函数组件)
  • 组件代码少于20行且逻辑简单(避免过度设计)
  • 项目已有既定的不同规范(一致性优先于完美)

The Ideal Structure

理想的组件结构

Components should follow this seven-section pattern:
tsx
// 1. IMPORTS (organized by source)
import React, { useState, useEffect } from 'react';
import { useQuery } from 'react-query';

import { formatDate } from '@/utils/date';
import { api } from '@/services/api';

import { Button } from './Button';

// 2. STYLED COMPONENTS (prefixed with "Styled")
const StyledContainer = styled.div`
  padding: 1rem;
  background: white;
`;

// 3. TYPE DEFINITIONS (ComponentNameProps pattern)
type UserProfileProps = {
  userId: string;
  onUpdate?: (user: User) => void;
};

// 4. COMPONENT FUNCTION
export const UserProfile = ({ userId, onUpdate }: UserProfileProps): JSX.Element => {
  // 5. LOGIC SECTIONS (in this order)
  // - Local state
  // - Custom/data hooks
  // - useEffect/useLayoutEffect
  // - Post-processing
  // - Callback handlers

  // 6. CONDITIONAL RENDERING (exit early)
  if (isLoading) return <Loading />;
  if (error) return <Error message={error.message} />;
  if (!data) return <Empty />;

  // 7. DEFAULT RENDER (success state)
  return (
    <StyledContainer>
      {/* Main component JSX */}
    </StyledContainer>
  );
};
JavaScript: Same pattern without type annotations (skip Section 3 or use JSDoc).
组件应遵循以下七段式结构:
tsx
// 1. 导入(按来源分类)
import React, { useState, useEffect } from 'react';
import { useQuery } from 'react-query';

import { formatDate } from '@/utils/date';
import { api } from '@/services/api';

import { Button } from './Button';

// 2. 样式组件(以"Styled"为前缀)
const StyledContainer = styled.div`
  padding: 1rem;
  background: white;
`;

// 3. 类型定义(采用ComponentNameProps命名模式)
type UserProfileProps = {
  userId: string;
  onUpdate?: (user: User) => void;
};

// 4. 组件函数
export const UserProfile = ({ userId, onUpdate }: UserProfileProps): JSX.Element => {
  // 5. 逻辑部分(按以下顺序)
  // - 本地状态
  // - 自定义/数据Hooks
  // - useEffect/useLayoutEffect
  // - 后处理逻辑
  // - 回调处理函数

  // 6. 条件渲染(提前退出)
  if (isLoading) return <Loading />;
  if (error) return <Error message={error.message} />;
  if (!data) return <Empty />;

  // 7. 默认渲染(成功状态)
  return (
    <StyledContainer>
      {/* 主组件JSX */}
    </StyledContainer>
  );
};
JavaScript版本: 采用相同模式,无需类型注解(可跳过第3部分或使用JSDoc)。

Section 1: Import Organization

第1部分:导入组织

Order imports by source to reduce cognitive load:
tsx
// ✅ Good: Clear grouping with blank lines
import React, { useState, useEffect, useMemo } from 'react';
import { useQuery, useMutation } from 'react-query';
import { format } from 'date-fns';

import { api } from '@/services/api';
import { formatCurrency } from '@/utils/format';

import { Button } from './Button';
import { Card } from './Card';
tsx
// ❌ Bad: Random order, no grouping
import { Button } from './Button';
import { format } from 'date-fns';
import React, { useState } from 'react';
import { api } from '@/services/api';
import { useQuery } from 'react-query';
Import priority:
  1. React imports (first)
  2. Third-party libraries (followed by blank line)
  3. Internal/aliased imports (
    @/
    ) (followed by blank line)
  4. Local component imports (same directory)
按来源排序导入,降低认知负担:
tsx
// ✅ 推荐:分组清晰,空行分隔
import React, { useState, useEffect, useMemo } from 'react';
import { useQuery, useMutation } from 'react-query';
import { format } from 'date-fns';

import { api } from '@/services/api';
import { formatCurrency } from '@/utils/format';

import { Button } from './Button';
import { Card } from './Card';
tsx
// ❌ 不推荐:顺序混乱,无分组
import { Button } from './Button';
import { format } from 'date-fns';
import React, { useState } from 'react';
import { api } from '@/services/api';
import { useQuery } from 'react-query';
导入优先级:
  1. React导入(放在最前面)
  2. 第三方库导入(后接空行)
  3. 内部/别名导入(
    @/
    )(后接空行)
  4. 本地组件导入(同一目录)

Section 2: Styling

第2部分:样式处理

The key principle is separating styling from logic. The approach depends on your styling solution:
styled-components / emotion: Prefix with
Styled
for instant recognition:
<Good> ```tsx const StyledCard = styled.div` border: 1px solid #ccc; border-radius: 8px; padding: 1rem; `;
const StyledTitle = styled.h2
  font-size: 1.5rem;   margin-bottom: 0.5rem;
;
export const Card = ({ title, children }) => ( <StyledCard> <StyledTitle>{title}</StyledTitle> {children} </StyledCard> );
</Good>

<Bad>
```tsx
// ❌ Bad: Can't tell if CardWrapper is styled or contains logic
const CardWrapper = styled.div`
  border: 1px solid #ccc;
`;

const Title = styled.h2`
  font-size: 1.5rem;
`;
</Bad>
When styled components grow large:
  • Move to co-located
    ComponentName.styled.ts
    file
  • Import as
    import * as S from './ComponentName.styled'
  • Use as
    <S.Container>
    ,
    <S.Title>
    , etc.
Tailwind CSS: Extract repeated utility sets into components or use
@apply
:
tsx
// Wrapper component keeps JSX clean
const Card = ({ title, children }: CardProps) => (
  <div className="border border-gray-300 rounded-lg p-4">
    <h2 className="text-xl mb-2">{title}</h2>
    {children}
  </div>
);
CSS Modules: Import as
styles
and use bracket notation:
tsx
import styles from './Card.module.css';

const Card = ({ title, children }: CardProps) => (
  <div className={styles.container}>
    <h2 className={styles.title}>{title}</h2>
    {children}
  </div>
);
JavaScript: Same patterns work for
.js
/
.jsx
files.
核心原则:将样式与逻辑分离。具体方式取决于你使用的样式方案:
styled-components / emotion: 以"Styled"为前缀,便于快速识别:
<Good> ```tsx const StyledCard = styled.div` border: 1px solid #ccc; border-radius: 8px; padding: 1rem; `;
const StyledTitle = styled.h2
  font-size: 1.5rem;   margin-bottom: 0.5rem;
;
export const Card = ({ title, children }) => ( <StyledCard> <StyledTitle>{title}</StyledTitle> {children} </StyledCard> );
</Good>

<Bad>
```tsx
// ❌ 不推荐:无法判断CardWrapper是样式组件还是包含逻辑
const CardWrapper = styled.div`
  border: 1px solid #ccc;
`;

const Title = styled.h2`
  font-size: 1.5rem;
`;
</Bad>
当样式组件代码过多时:
  • 移至同目录下的
    ComponentName.styled.ts
    文件
  • 通过
    import * as S from './ComponentName.styled'
    导入
  • 使用
    <S.Container>
    <S.Title>
    等方式调用
Tailwind CSS: 将重复的工具类集合提取为组件或使用
@apply
tsx
// 包装组件让JSX更简洁
const Card = ({ title, children }: CardProps) => (
  <div className="border border-gray-300 rounded-lg p-4">
    <h2 className="text-xl mb-2">{title}</h2>
    {children}
  </div>
);
CSS Modules: 导入为
styles
并使用括号语法:
tsx
import styles from './Card.module.css';

const Card = ({ title, children }: CardProps) => (
  <div className={styles.container}>
    <h2 className={styles.title}>{title}</h2>
    {children}
  </div>
);
JavaScript版本: 相同模式适用于
.js
/
.jsx
文件。

Section 3: Type Definitions

第3部分:类型定义

Declare types immediately above the component for visibility:
<Good> ```tsx type ButtonProps = { variant?: 'primary' | 'secondary'; size?: 'sm' | 'md' | 'lg'; onClick: () => void; children: React.ReactNode; };
export const Button = ({ variant = 'primary', size = 'md', onClick, children }: ButtonProps): JSX.Element => { // Component logic };
</Good>

<Bad>
```tsx
// ❌ Bad: Inline types hide the API
export const Button = ({ variant, size, onClick, children }: {
  variant?: 'primary' | 'secondary'; size?: 'sm' | 'md' | 'lg';
  onClick: () => void; children: React.ReactNode;
}) => { /* ... */ };
</Bad>
Naming: Props:
ComponentNameProps
. Return types:
JSX.Element
(or custom:
ComponentNameReturn
).
JavaScript: Use JSDoc
@typedef
and
@param
annotations for equivalent documentation.
Why: Makes component API visible at a glance, easier to modify without disturbing component code, better for documentation.
在组件上方紧邻声明类型,提升可见性:
<Good> ```tsx type ButtonProps = { variant?: 'primary' | 'secondary'; size?: 'sm' | 'md' | 'lg'; onClick: () => void; children: React.ReactNode; };
export const Button = ({ variant = 'primary', size = 'md', onClick, children }: ButtonProps): JSX.Element => { // 组件逻辑 };
</Good>

<Bad>
```tsx
// ❌ 不推荐:内联类型隐藏了组件API
export const Button = ({ variant, size, onClick, children }: {
  variant?: 'primary' | 'secondary'; size?: 'sm' | 'md' | 'lg';
  onClick: () => void; children: React.ReactNode;
}) => { /* ... */ };
</Bad>
命名规则: Props类型采用
ComponentNameProps
;返回类型采用
JSX.Element
(或自定义的
ComponentNameReturn
)。
JavaScript版本: 使用JSDoc的
@typedef
@param
注解实现等效的文档说明。
优势: 让组件API一目了然,无需修改组件代码即可轻松修改类型,更便于文档编写。

Section 4: Component Function

第4部分:组件函数

Use named exports with const arrow functions:
<Good> ```tsx export const UserProfile = ({ userId }: UserProfileProps): JSX.Element => { // Component logic }; ``` </Good> <Bad> ```tsx // ❌ Bad: Default export makes refactoring harder export default function UserProfile({ userId }: UserProfileProps): JSX.Element { // Component logic } ``` </Bad>
Why const + arrow functions:
  • Easy to wrap with
    useCallback
    later if needed
  • Consistent with other hooks and callbacks in component
  • Named exports are easier to refactor and search for
JavaScript: Same pattern without type annotations.
使用具名导出的箭头函数:
<Good> ```tsx export const UserProfile = ({ userId }: UserProfileProps): JSX.Element => { // 组件逻辑 }; ``` </Good> <Bad> ```tsx // ❌ 不推荐:默认导出增加重构难度 export default function UserProfile({ userId }: UserProfileProps): JSX.Element { // 组件逻辑 } ``` </Bad>
使用const箭头函数的原因:
  • 后续需要时可轻松用
    useCallback
    包装
  • 与组件内的其他Hooks和回调保持一致
  • 具名导出更便于重构和搜索
JavaScript版本: 采用相同模式,无需类型注解。

Section 5: Logic Flow

第5部分:逻辑流程

Organize component logic in this strict order:
tsx
export const UserProfile = ({ userId }: UserProfileProps): JSX.Element => {
  // 5.1 - LOCAL STATE
  const [isEditing, setIsEditing] = useState(false);
  const inputRef = useRef<HTMLInputElement>(null);

  // 5.2 - CUSTOM/DATA HOOKS
  const { data: user, isLoading, error } = useQuery(['user', userId], () => api.getUser(userId));
  const { mutate: updateUser } = useMutation(api.updateUser);

  // 5.3 - useEffect/useLayoutEffect
  useEffect(() => {
    if (isEditing && inputRef.current) inputRef.current.focus();
  }, [isEditing]);

  // 5.4 - POST-PROCESSING
  const displayName = user ? `${user.firstName} ${user.lastName}` : '';

  // 5.5 - CALLBACK HANDLERS
  const handleEdit = () => setIsEditing(true);
  const handleSave = (updates: Partial<User>) => { updateUser(updates); setIsEditing(false); };

  // [Next: Conditional rendering, then Default render]
};
Why this order: Respects React's hook rules, puts dependent logic after dependencies, makes component flow easy to trace.
JavaScript: Same ordering applies without type annotations.
严格按照以下顺序组织组件逻辑:
tsx
export const UserProfile = ({ userId }: UserProfileProps): JSX.Element => {
  // 5.1 - 本地状态
  const [isEditing, setIsEditing] = useState(false);
  const inputRef = useRef<HTMLInputElement>(null);

  // 5.2 - 自定义/数据Hooks
  const { data: user, isLoading, error } = useQuery(['user', userId], () => api.getUser(userId));
  const { mutate: updateUser } = useMutation(api.updateUser);

  // 5.3 - useEffect/useLayoutEffect
  useEffect(() => {
    if (isEditing && inputRef.current) inputRef.current.focus();
  }, [isEditing]);

  // 5.4 - 后处理逻辑
  const displayName = user ? `${user.firstName} ${user.lastName}` : '';

  // 5.5 - 回调处理函数
  const handleEdit = () => setIsEditing(true);
  const handleSave = (updates: Partial<User>) => { updateUser(updates); setIsEditing(false); };

  // [下一步:条件渲染,然后是默认渲染]
};
为什么采用此顺序: 遵循React的Hooks规则,将依赖逻辑放在依赖项之后,让组件流程易于追踪。
JavaScript版本: 无需类型注解,顺序规则相同。

Section 6: Conditional Rendering

第6部分:条件渲染

Exit early for loading, error, and empty states:
<Good> ```tsx // Exit early - each conditional gets own return if (isLoading) return <LoadingSpinner />; if (error) return <ErrorMessage message={error.message} />; if (!data) return <EmptyState message="User not found" />;
// Success state continues below return <div>{/* Main component JSX */}</div>;
</Good>

<Bad>
```tsx
// ❌ Bad: Nested ternaries are hard to read
return (
  <div>
    {isLoading ? <LoadingSpinner /> : error ? <ErrorMessage /> : !data ? <EmptyState /> : (
      <div>{/* Main component JSX buried deep */}</div>
    )}
  </div>
);
</Bad>
Benefits of early returns:
  • Reduces nesting depth
  • Main success render stays at bottom (most important case)
  • Each condition is independent and easy to test
  • TypeScript can narrow types after guards
JavaScript: Same pattern applies.
对加载、错误和空状态提前返回:
<Good> ```tsx // 提前返回 - 每个条件单独处理 if (isLoading) return <LoadingSpinner />; if (error) return <ErrorMessage message={error.message} />; if (!data) return <EmptyState message="用户未找到" />;
// 成功状态继续执行以下代码 return <div>{/* 主组件JSX */}</div>;
</Good>

<Bad>
```tsx
// ❌ 不推荐:嵌套三元表达式难以阅读
return (
  <div>
    {isLoading ? <LoadingSpinner /> : error ? <ErrorMessage /> : !data ? <EmptyState /> : (
      <div>{/* 主组件JSX被深埋 */}</div>
    )}
  </div>
);
</Bad>
提前返回的优势:
  • 减少嵌套层级
  • 主成功渲染保持在底部(最重要的场景)
  • 每个条件独立且易于测试
  • TypeScript可在类型守卫后缩小类型范围
JavaScript版本: 模式相同。

Section 7: Default Render

第7部分:默认渲染

Keep the success/default render at the bottom, after all early returns:
tsx
  // Success state - the main component render
  return (
    <StyledContainer>
      <StyledHeader>
        <StyledTitle>{displayName}</StyledTitle>
        <Button onClick={handleEdit}>Edit</Button>
      </StyledHeader>
      {isEditing ? (
        <EditForm user={user} onSave={handleSave} onCancel={handleCancel} />
      ) : (
        <UserDetails user={user} />
      )}
    </StyledContainer>
  );
Why: Most important case (happy path) is most visible. All error states eliminated, all data and handlers already declared.
将成功/默认渲染放在所有提前返回之后,位于底部:
tsx
  // 成功状态 - 组件主渲染
  return (
    <StyledContainer>
      <StyledHeader>
        <StyledTitle>{displayName}</StyledTitle>
        <Button onClick={handleEdit}>编辑</Button>
      </StyledHeader>
      {isEditing ? (
        <EditForm user={user} onSave={handleSave} onCancel={handleCancel} />
      ) : (
        <UserDetails user={user} />
      )}
    </StyledContainer>
  );
原因: 最重要的场景(正常流程)最显眼,所有错误状态已被排除,所有数据和处理函数已声明完毕。

Refactoring: Extract to Custom Hooks

重构:提取为自定义Hooks

When components grow complex, extract logic into custom hooks:
<Good> ```tsx // usePost.ts - All logic extracted into a custom hook export const usePost = (postId: string) => { const [isEditing, setIsEditing] = useState(false); const { data: post, isLoading, error } = useQuery(['post', postId], () => api.getPost(postId)); const { mutate: updatePost } = useMutation(api.updatePost);
const handleEdit = () => setIsEditing(true); const handleSave = (updates: Partial<Post>) => { updatePost(updates); setIsEditing(false); };
return { post, isLoading, error, isEditing, handleEdit, handleSave }; };
// PostView.tsx - Clean component focused on presentation export const PostView = ({ postId }: PostViewProps): JSX.Element => { const { post, isLoading, error, isEditing, handleEdit, handleSave } = usePost(postId);
if (isLoading) return <Loading />; if (error) return <Error message={error.message} />; if (!post) return <Empty />;
return <StyledContainer>{/* Presentation-focused JSX */}</StyledContainer>; };
</Good>

**When to extract to custom hooks:**
- Component logic exceeds 50 lines
- State management becomes complex
- Multiple effects interact
- Logic is reusable across components
- Component file exceeds 200 lines

**Hook naming:** `use[Domain]` pattern (e.g., `usePost`, `useAuth`, `useCart`)

**JavaScript:** Same pattern without type annotations.
当组件逻辑复杂时,将逻辑提取为自定义Hooks:
<Good> ```tsx // usePost.ts - 所有逻辑提取到自定义Hook中 export const usePost = (postId: string) => { const [isEditing, setIsEditing] = useState(false); const { data: post, isLoading, error } = useQuery(['post', postId], () => api.getPost(postId)); const { mutate: updatePost } = useMutation(api.updatePost);
const handleEdit = () => setIsEditing(true); const handleSave = (updates: Partial<Post>) => { updatePost(updates); setIsEditing(false); };
return { post, isLoading, error, isEditing, handleEdit, handleSave }; };
// PostView.tsx - 简洁的组件专注于展示 export const PostView = ({ postId }: PostViewProps): JSX.Element => { const { post, isLoading, error, isEditing, handleEdit, handleSave } = usePost(postId);
if (isLoading) return <Loading />; if (error) return <Error message={error.message} />; if (!post) return <Empty />;
return <StyledContainer>{/* 专注于展示的JSX */}</StyledContainer>; };
</Good>

**何时提取为自定义Hooks:**
- 组件逻辑超过50行
- 状态管理变得复杂
- 多个Effects相互作用
- 逻辑可在多个组件间复用
- 组件文件超过200行

**Hook命名规则:** 采用`use[领域]`模式(例如`usePost`、`useAuth`、`useCart`)

**JavaScript版本:** 无需类型注解,模式相同。

Common Hooks Antipatterns (Quick Reference)

常见Hooks反模式(速查)

These are the most frequent causes of infinite loops, stale data, and unexpected re-renders:
1. useEffect as onChange callback - Causes double renders or infinite loops:
tsx
// ❌ Bad: Effect syncs state derived from other state
useEffect(() => { setFullName(`${first} ${last}`); }, [first, last]);

// ✅ Good: Derive during render instead
const fullName = `${first} ${last}`;
2. useState initial value not updating with props:
tsx
// ❌ Bad: Initial value only runs once, won't track prop changes
const [value, setValue] = useState(props.initialValue);

// ✅ Good: Use a key to reset, or useEffect to sync
<Component key={itemId} initialValue={data.value} />
3. Non-exhaustive dependency arrays - Causes stale closures:
tsx
// ❌ Bad: Missing dependency means stale count value
useEffect(() => { setTotal(count * price); }, [price]);

// ✅ Good: Include all dependencies
useEffect(() => { setTotal(count * price); }, [count, price]);
For detailed explanations and more patterns, see React Hooks Antipatterns.
这些是导致无限循环、数据过期和意外重渲染的最常见原因:
1. 将useEffect用作onChange回调 - 导致双重渲染或无限循环:
tsx
// ❌ 不推荐:Effect同步从其他状态派生的状态
useEffect(() => { setFullName(`${first} ${last}`); }, [first, last]);

// ✅ 推荐:在渲染时直接派生
const fullName = `${first} ${last}`;
2. useState初始值未随props更新:
tsx
// ❌ 不推荐:初始值仅执行一次,不会跟踪props变化
const [value, setValue] = useState(props.initialValue);

// ✅ 推荐:使用key重置,或用useEffect同步
<Component key={itemId} initialValue={data.value} />
3. 依赖数组不完整 - 导致闭包过期:
tsx
// ❌ 不推荐:缺少依赖项导致count值过期
useEffect(() => { setTotal(count * price); }, [price]);

// ✅ 推荐:包含所有依赖项
useEffect(() => { setTotal(count * price); }, [count, price]);
如需详细解释和更多模式,请查看**React Hooks反模式**。

Deep Reference

深度参考

  • Complete Component Examples - Full TypeScript and JavaScript component examples
Only load these when specifically needed to save context.
  • 完整组件示例 - 完整的TypeScript和JavaScript组件示例
仅在特别需要时加载这些内容,以节省上下文空间。

Quick Reference

速查表

SectionWhat Goes HereWhy
1. ImportsReact, libraries, internal, localEasy to find dependencies
2. StylingStyled components, Tailwind, CSS ModulesVisual separation from logic
3. Type Definitions
*Props
,
*Return
types
Component API visibility
4. Component Function
export const Component =
Named exports for refactoring
5. Logic FlowState -> Hooks -> Effects -> HandlersRespects hook rules, logical order
6. Conditional RenderingEarly returns for edge casesReduces nesting
7. Default RenderSuccess state JSXMost important case most visible
部分内容原因
1. 导入React、第三方库、内部、本地组件便于查找依赖
2. 样式样式组件、Tailwind、CSS Modules视觉上与逻辑分离
3. 类型定义
*Props
*Return
类型
组件API可见性
4. 组件函数
export const Component =
具名导出便于重构
5. 逻辑流程状态 -> Hooks -> Effects -> 处理函数遵循Hooks规则,逻辑顺序清晰
6. 条件渲染异常场景提前返回减少嵌套
7. 默认渲染成功状态JSX最重要的场景最显眼

Troubleshooting

故障排查

Problem: Component is getting too long (> 200 lines)

问题:组件代码过长(>200行)

Cause: Too much logic in one file
Solution:
  1. Extract data fetching to custom hook (
    useUserProfile
    )
  2. Move styled components to
    ComponentName.styled.ts
  3. Split into smaller sub-components
  4. Extract complex calculations to utility functions
原因: 单个文件中包含过多逻辑
解决方案:
  1. 将数据获取逻辑提取为自定义Hook(
    useUserProfile
  2. 将样式组件移至
    ComponentName.styled.ts
  3. 拆分为更小的子组件
  4. 将复杂计算提取为工具函数

Problem: Can't decide if something should be a styled component or a sub-component

问题:无法确定应该使用样式组件还是子组件

Solution:
  • Styled component if it only adds styling (no props, no logic)
  • Sub-component if it has its own props, state, or logic
解决方案:
  • 样式组件:仅添加样式(无props、无逻辑)
  • 子组件:拥有自己的props、状态或逻辑

Problem: TypeScript types getting complex

问题:TypeScript类型过于复杂

Solution: Split component into smaller pieces, extract shared types to
types.ts
, use utility types (
Pick
,
Omit
,
Partial
).
解决方案: 将组件拆分为更小的部分,将共享类型提取到
types.ts
,使用工具类型(
Pick
Omit
Partial
)。

Problem: Hooks causing infinite re-render loop, stale data, or state not syncing

问题:Hooks导致无限重渲染循环、数据过期或状态不同步

Solution: See the Common Hooks Antipatterns section above for the top 3 patterns, or load React Hooks Antipatterns for the full guide.
解决方案: 查看上述常见Hooks反模式部分中的前3种模式,或加载**React Hooks反模式**获取完整指南。

Variations and Flexibility

变体与灵活性

This is a pattern, not a law. Adapt as needed:
  • Small components (< 50 lines) can skip some structure
  • Simple components without state can skip logic sections
  • React Server Components don't use hooks or client state - skip logic sections, focus on data fetching and render
这是一种模式,而非硬性规定。可根据需要调整:
  • 小型组件(<50行)可跳过部分结构
  • 简单组件(无状态)可跳过逻辑部分
  • React Server Components 不使用Hooks或客户端状态 - 跳过逻辑部分,专注于数据获取和渲染

Integration

集成

Works with: styled-components, emotion, Tailwind CSS, CSS Modules, React Query / TanStack Query, SWR, Zustand / Redux
Pairs well with: ESLint (
eslint-plugin-import
), Prettier, TypeScript, Storybook, Vitest / Jest
兼容工具: styled-components、emotion、Tailwind CSS、CSS Modules、React Query / TanStack Query、SWR、Zustand / Redux
推荐搭配: ESLint(
eslint-plugin-import
)、Prettier、TypeScript、Storybook、Vitest / Jest

References

参考资料