Loading...
Loading...
Compare original and translation side by side
// 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>
);
};// 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>
);
};// ✅ 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';// ❌ 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 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';// ❌ 不推荐:顺序混乱,无分组
import { Button } from './Button';
import { format } from 'date-fns';
import React, { useState } from 'react';
import { api } from '@/services/api';
import { useQuery } from 'react-query';@/Styled font-size: 1.5rem; margin-bottom: 0.5rem;</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;
`;ComponentName.styled.tsimport * as S from './ComponentName.styled'<S.Container><S.Title>@apply// 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>
);stylesimport styles from './Card.module.css';
const Card = ({ title, children }: CardProps) => (
<div className={styles.container}>
<h2 className={styles.title}>{title}</h2>
{children}
</div>
);.js.jsx font-size: 1.5rem; margin-bottom: 0.5rem;</Good>
<Bad>
```tsx
// ❌ 不推荐:无法判断CardWrapper是样式组件还是包含逻辑
const CardWrapper = styled.div`
border: 1px solid #ccc;
`;
const Title = styled.h2`
font-size: 1.5rem;
`;ComponentName.styled.tsimport * as S from './ComponentName.styled'<S.Container><S.Title>@apply// 包装组件让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>
);stylesimport styles from './Card.module.css';
const Card = ({ title, children }: CardProps) => (
<div className={styles.container}>
<h2 className={styles.title}>{title}</h2>
{children}
</div>
);.js.jsx</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;
}) => { /* ... */ };ComponentNamePropsJSX.ElementComponentNameReturn@typedef@param</Good>
<Bad>
```tsx
// ❌ 不推荐:内联类型隐藏了组件API
export const Button = ({ variant, size, onClick, children }: {
variant?: 'primary' | 'secondary'; size?: 'sm' | 'md' | 'lg';
onClick: () => void; children: React.ReactNode;
}) => { /* ... */ };ComponentNamePropsJSX.ElementComponentNameReturn@typedef@paramuseCallbackuseCallbackexport 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]
};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); };
// [下一步:条件渲染,然后是默认渲染]
};</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>
);</Good>
<Bad>
```tsx
// ❌ 不推荐:嵌套三元表达式难以阅读
return (
<div>
{isLoading ? <LoadingSpinner /> : error ? <ErrorMessage /> : !data ? <EmptyState /> : (
<div>{/* 主组件JSX被深埋 */}</div>
)}
</div>
); // 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>
); // 成功状态 - 组件主渲染
return (
<StyledContainer>
<StyledHeader>
<StyledTitle>{displayName}</StyledTitle>
<Button onClick={handleEdit}>编辑</Button>
</StyledHeader>
{isEditing ? (
<EditForm user={user} onSave={handleSave} onCancel={handleCancel} />
) : (
<UserDetails user={user} />
)}
</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.</Good>
**何时提取为自定义Hooks:**
- 组件逻辑超过50行
- 状态管理变得复杂
- 多个Effects相互作用
- 逻辑可在多个组件间复用
- 组件文件超过200行
**Hook命名规则:** 采用`use[领域]`模式(例如`usePost`、`useAuth`、`useCart`)
**JavaScript版本:** 无需类型注解,模式相同。// ❌ Bad: Effect syncs state derived from other state
useEffect(() => { setFullName(`${first} ${last}`); }, [first, last]);
// ✅ Good: Derive during render instead
const fullName = `${first} ${last}`;// ❌ 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} />// ❌ Bad: Missing dependency means stale count value
useEffect(() => { setTotal(count * price); }, [price]);
// ✅ Good: Include all dependencies
useEffect(() => { setTotal(count * price); }, [count, price]);// ❌ 不推荐:Effect同步从其他状态派生的状态
useEffect(() => { setFullName(`${first} ${last}`); }, [first, last]);
// ✅ 推荐:在渲染时直接派生
const fullName = `${first} ${last}`;// ❌ 不推荐:初始值仅执行一次,不会跟踪props变化
const [value, setValue] = useState(props.initialValue);
// ✅ 推荐:使用key重置,或用useEffect同步
<Component key={itemId} initialValue={data.value} />// ❌ 不推荐:缺少依赖项导致count值过期
useEffect(() => { setTotal(count * price); }, [price]);
// ✅ 推荐:包含所有依赖项
useEffect(() => { setTotal(count * price); }, [count, price]);| Section | What Goes Here | Why |
|---|---|---|
| 1. Imports | React, libraries, internal, local | Easy to find dependencies |
| 2. Styling | Styled components, Tailwind, CSS Modules | Visual separation from logic |
| 3. Type Definitions | | Component API visibility |
| 4. Component Function | | Named exports for refactoring |
| 5. Logic Flow | State -> Hooks -> Effects -> Handlers | Respects hook rules, logical order |
| 6. Conditional Rendering | Early returns for edge cases | Reduces nesting |
| 7. Default Render | Success state JSX | Most important case most visible |
| 部分 | 内容 | 原因 |
|---|---|---|
| 1. 导入 | React、第三方库、内部、本地组件 | 便于查找依赖 |
| 2. 样式 | 样式组件、Tailwind、CSS Modules | 视觉上与逻辑分离 |
| 3. 类型定义 | | 组件API可见性 |
| 4. 组件函数 | | 具名导出便于重构 |
| 5. 逻辑流程 | 状态 -> Hooks -> Effects -> 处理函数 | 遵循Hooks规则,逻辑顺序清晰 |
| 6. 条件渲染 | 异常场景提前返回 | 减少嵌套 |
| 7. 默认渲染 | 成功状态JSX | 最重要的场景最显眼 |
useUserProfileComponentName.styled.tsuseUserProfileComponentName.styled.tstypes.tsPickOmitPartialtypes.tsPickOmitPartialeslint-plugin-importeslint-plugin-import