react
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact Core Patterns
React核心模式
Purpose
用途
Essential React 19 patterns for building modern applications with hooks, Suspense, lazy loading, and TypeScript.
Note: React 19 (released December 2024) breaking changes:
- no longer needed - pass
forwardRefas a prop directlyref - removed (silently ignored)
propTypes - New JSX transform required
- type discouraged - use direct function components instead
React.FC
使用Hooks、Suspense、懒加载和TypeScript构建现代应用的React 19必备模式。
注意:React 19(2024年12月发布)的破坏性变更:
- 不再需要——直接将
forwardRef作为props传递ref - 已移除(会被静默忽略)
propTypes - 需要新的JSX转换器
- 不推荐使用类型——改用直接函数组件
React.FC
When to Use This Skill
何时使用此技能
- Creating React components
- Using React hooks (useState, useEffect, useCallback, useMemo)
- Implementing lazy loading and code splitting
- Working with Suspense boundaries
- React-specific TypeScript patterns
- Performance optimization with React
- 创建React组件
- 使用React Hooks(useState、useEffect、useCallback、useMemo)
- 实现懒加载和代码分割
- 处理Suspense边界
- React专属TypeScript模式
- React性能优化
Quick Start
快速开始
Component Structure Template
组件结构模板
typescript
import { useState, useCallback } from 'react';
interface Props {
userId: string;
onUpdate?: (data: UserData) => void;
}
interface UserData {
name: string;
email: string;
}
function UserProfile({ userId, onUpdate }: Props) {
const [data, setData] = useState<UserData | null>(null);
const handleUpdate = useCallback((newData: UserData) => {
setData(newData);
onUpdate?.(newData);
}, [onUpdate]);
return (
<div>
{/* Component content */}
</div>
);
}
export default UserProfile;typescript
import { useState, useCallback } from 'react';
interface Props {
userId: string;
onUpdate?: (data: UserData) => void;
}
interface UserData {
name: string;
email: string;
}
function UserProfile({ userId, onUpdate }: Props) {
const [data, setData] = useState<UserData | null>(null);
const handleUpdate = useCallback((newData: UserData) => {
setData(newData);
onUpdate?.(newData);
}, [onUpdate]);
return (
<div>
{/* Component content */}
</div>
);
}
export default UserProfile;Component Checklist
组件检查清单
Creating a React component? Follow this:
- Use function components with typed props (not )
React.FC - Define interfaces for Props and local state
- Use for event handlers passed to children
useCallback - Use for expensive computations
useMemo - Lazy load if heavy component:
lazy(() => import()) - Wrap lazy components in with fallback
<Suspense> - Default export at bottom
- No conditional hooks (hooks must be called in same order)
- Pass as a prop (no
refneeded in React 19)forwardRef
创建React组件时,请遵循以下要求:
- 使用带类型props的函数组件(不要用)
React.FC - 为Props和本地状态定义接口
- 传递给子组件的事件处理函数使用包裹
useCallback - 昂贵的计算使用
useMemo - 如果是重型组件则懒加载:
lazy(() => import()) - 用包裹懒加载组件并设置fallback
<Suspense> - 在底部使用默认导出
- 不要有条件式调用Hooks(Hooks必须按相同顺序调用)
- 将作为props传递(React 19中不需要
ref)forwardRef
Core Hooks Patterns
核心Hooks模式
useState
useState
typescript
// Simple state
const [count, setCount] = useState<number>(0);
// Object state
const [user, setUser] = useState<User | null>(null);
// Array state
const [items, setItems] = useState<Item[]>([]);
// Functional updates when depending on previous state
setCount(prev => prev + 1);
setItems(prev => [...prev, newItem]);typescript
// Simple state
const [count, setCount] = useState<number>(0);
// Object state
const [user, setUser] = useState<User | null>(null);
// Array state
const [items, setItems] = useState<Item[]>([]);
// Functional updates when depending on previous state
setCount(prev => prev + 1);
setItems(prev => [...prev, newItem]);useCallback
useCallback
typescript
// Wrap functions passed to child components
const handleClick = useCallback((id: string) => {
console.log('Clicked:', id);
}, []); // Empty deps if no dependencies
// With dependencies
const handleUpdate = useCallback((data: FormData) => {
apiCall(userId, data);
}, [userId]); // Re-create when userId changestypescript
// Wrap functions passed to child components
const handleClick = useCallback((id: string) => {
console.log('Clicked:', id);
}, []); // Empty deps if no dependencies
// With dependencies
const handleUpdate = useCallback((data: FormData) => {
apiCall(userId, data);
}, [userId]); // Re-create when userId changesuseMemo
useMemo
typescript
// Expensive computation
const sortedItems = useMemo(() => {
return items.sort((a, b) => a.score - b.score);
}, [items]);
// Derived state
const totalPrice = useMemo(() => {
return cart.reduce((sum, item) => sum + item.price, 0);
}, [cart]);typescript
// Expensive computation
const sortedItems = useMemo(() => {
return items.sort((a, b) => a.score - b.score);
}, [items]);
// Derived state
const totalPrice = useMemo(() => {
return cart.reduce((sum, item) => sum + item.price, 0);
}, [cart]);useEffect
useEffect
typescript
// Run once on mount
useEffect(() => {
fetchData();
}, []);
// Run when dependency changes
useEffect(() => {
if (userId) {
loadUserData(userId);
}
}, [userId]);
// Cleanup
useEffect(() => {
const subscription = subscribe(userId);
return () => subscription.unsubscribe();
}, [userId]);typescript
// Run once on mount
useEffect(() => {
fetchData();
}, []);
// Run when dependency changes
useEffect(() => {
if (userId) {
loadUserData(userId);
}
}, [userId]);
// Cleanup
useEffect(() => {
const subscription = subscribe(userId);
return () => subscription.unsubscribe();
}, [userId]);Lazy Loading & Code Splitting
懒加载与代码分割
Basic Lazy Loading
基础懒加载
typescript
import React, { Suspense } from 'react';
// Lazy load heavy component
const HeavyChart = React.lazy(() => import('./HeavyChart'));
function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<div>Loading chart...</div>}>
<HeavyChart />
</Suspense>
</div>
);
}typescript
import React, { Suspense } from 'react';
// Lazy load heavy component
const HeavyChart = React.lazy(() => import('./HeavyChart'));
function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<div>Loading chart...</div>}>
<HeavyChart />
</Suspense>
</div>
);
}Multiple Lazy Components
多组件懒加载
typescript
const AdminPanel = React.lazy(() => import('./AdminPanel'));
const UserSettings = React.lazy(() => import('./UserSettings'));
const Reports = React.lazy(() => import('./Reports'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/admin" element={<AdminPanel />} />
<Route path="/settings" element={<UserSettings />} />
<Route path="/reports" element={<Reports />} />
</Routes>
</Suspense>
);
}typescript
const AdminPanel = React.lazy(() => import('./AdminPanel'));
const UserSettings = React.lazy(() => import('./UserSettings'));
const Reports = React.lazy(() => import('./Reports'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/admin" element={<AdminPanel />} />
<Route path="/settings" element={<UserSettings />} />
<Route path="/reports" element={<Reports />} />
</Routes>
</Suspense>
);
}Feature-Based Code Splitting
基于功能的代码分割
typescript
// features/auth/index.tsx
export { default } from './AuthFeature';
// Lazy load entire feature
const AuthFeature = React.lazy(() => import('~/features/auth'));
<Suspense fallback={<FeatureLoader />}>
<AuthFeature />
</Suspense>typescript
// features/auth/index.tsx
export { default } from './AuthFeature';
// Lazy load entire feature
const AuthFeature = React.lazy(() => import('~/features/auth'));
<Suspense fallback={<FeatureLoader />}>
<AuthFeature />
</Suspense>Suspense Patterns
Suspense模式
Suspense Boundaries
Suspense边界
typescript
// Wrap data-fetching components
<Suspense fallback={<Skeleton />}>
<UserProfile userId={id} />
</Suspense>
// Nested Suspense for granular loading
<Suspense fallback={<PageLoader />}>
<Header />
<Suspense fallback={<ContentSkeleton />}>
<MainContent />
</Suspense>
<Footer />
</Suspense>typescript
// Wrap data-fetching components
<Suspense fallback={<Skeleton />}>
<UserProfile userId={id} />
</Suspense>
// Nested Suspense for granular loading
<Suspense fallback={<PageLoader />}>
<Header />
<Suspense fallback={<ContentSkeleton />}>
<MainContent />
</Suspense>
<Footer />
</Suspense>Error Boundaries with Suspense
结合Suspense的错误边界
typescript
import { ErrorBoundary } from 'react-error-boundary';
<ErrorBoundary fallback={<ErrorFallback />}>
<Suspense fallback={<Loading />}>
<DataComponent />
</Suspense>
</ErrorBoundary>typescript
import { ErrorBoundary } from 'react-error-boundary';
<ErrorBoundary fallback={<ErrorFallback />}>
<Suspense fallback={<Loading />}>
<DataComponent />
</Suspense>
</ErrorBoundary>TypeScript Patterns
TypeScript模式
Component Props
组件Props
typescript
// Basic props
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
}
// Props with children
interface CardProps {
title: string;
children: React.ReactNode;
}
// Props with specific child types
interface ListProps {
children: React.ReactElement<ItemProps> | React.ReactElement<ItemProps>[];
}
// Props with event handlers
interface FormProps {
onSubmit: (data: FormData) => void;
onChange?: (field: string, value: unknown) => void;
}typescript
// Basic props
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
}
// Props with children
interface CardProps {
title: string;
children: React.ReactNode;
}
// Props with specific child types
interface ListProps {
children: React.ReactElement<ItemProps> | React.ReactElement<ItemProps>[];
}
// Props with event handlers
interface FormProps {
onSubmit: (data: FormData) => void;
onChange?: (field: string, value: unknown) => void;
}Hooks TypeScript
Hooks的TypeScript用法
typescript
// useState with type
const [user, setUser] = useState<User | null>(null);
const [items, setItems] = useState<Item[]>([]);
// useRef with type
const inputRef = useRef<HTMLInputElement>(null);
const timerRef = useRef<number | null>(null);
// Custom hook with return type
function useUser(id: string): { user: User | null; loading: boolean } {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
// ... implementation
return { user, loading };
}typescript
// useState with type
const [user, setUser] = useState<User | null>(null);
const [items, setItems] = useState<Item[]>([]);
// useRef with type
const inputRef = useRef<HTMLInputElement>(null);
const timerRef = useRef<number | null>(null);
// Custom hook with return type
function useUser(id: string): { user: User | null; loading: boolean } {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
// ... implementation
return { user, loading };
}Performance Optimization
性能优化
React.memo
React.memo
typescript
// Memoize component to prevent unnecessary re-renders
const UserCard = React.memo<UserCardProps>(({ user, onUpdate }) => {
return (
<div>
<h3>{user.name}</h3>
<button onClick={() => onUpdate(user.id)}>Update</button>
</div>
);
});
// Custom comparison function
const UserCard = React.memo(UserCardComponent, (prevProps, nextProps) => {
return prevProps.user.id === nextProps.user.id;
});typescript
// Memoize component to prevent unnecessary re-renders
const UserCard = React.memo<UserCardProps>(({ user, onUpdate }) => {
return (
<div>
<h3>{user.name}</h3>
<button onClick={() => onUpdate(user.id)}>Update</button>
</div>
);
});
// Custom comparison function
const UserCard = React.memo(UserCardComponent, (prevProps, nextProps) => {
return prevProps.user.id === nextProps.user.id;
});Avoiding Re-renders
避免不必要的重渲染
typescript
// ❌ Bad: Creates new function on every render
function Parent() {
return <Child onClick={() => console.log('clicked')} />;
}
// ✅ Good: Stable function reference
function Parent() {
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
return <Child onClick={handleClick} />;
}typescript
// ❌ Bad: Creates new function on every render
function Parent() {
return <Child onClick={() => console.log('clicked')} />;
}
// ✅ Good: Stable function reference
function Parent() {
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
return <Child onClick={handleClick} />;
}Common Patterns
常见模式
Conditional Rendering
条件渲染
typescript
// Ternary operator
{isLoading ? <Spinner /> : <Content />}
// Logical AND
{error && <ErrorMessage error={error} />}
// Nullish coalescing
{user ?? <GuestView />}
// Early return for loading states
function Component() {
const { data } = useSomeHook();
// ❌ Avoid early returns for loading - breaks hooks rules
// Use Suspense instead
return <div>{data.map(...)}</div>;
}typescript
// Ternary operator
{isLoading ? <Spinner /> : <Content />}
// Logical AND
{error && <ErrorMessage error={error} />}
// Nullish coalescing
{user ?? <GuestView />}
// Early return for loading states
function Component() {
const { data } = useSomeHook();
// ❌ Avoid early returns for loading - breaks hooks rules
// Use Suspense instead
return <div>{data.map(...)}</div>;
}Lists and Keys
列表与Keys
typescript
// Always use stable keys
{items.map(item => (
<ItemCard key={item.id} item={item} />
))}
// Never use index as key if list can reorder
// ❌ Bad
{items.map((item, index) => (
<ItemCard key={index} item={item} />
))}typescript
// Always use stable keys
{items.map(item => (
<ItemCard key={item.id} item={item} />
))}
// Never use index as key if list can reorder
// ❌ Bad
{items.map((item, index) => (
<ItemCard key={index} item={item} />
))}File Organization
文件组织
Feature-Based Structure
基于功能的结构
src/
├── features/
│ ├── auth/
│ │ ├── components/
│ │ ├── hooks/
│ │ ├── types/
│ │ └── index.tsx
│ └── posts/
│ ├── components/
│ ├── hooks/
│ ├── types/
│ └── index.tsx
├── components/ # Shared components
├── hooks/ # Shared hooks
└── types/ # Shared typessrc/
├── features/
│ ├── auth/
│ │ ├── components/
│ │ ├── hooks/
│ │ ├── types/
│ │ └── index.tsx
│ └── posts/
│ ├── components/
│ ├── hooks/
│ ├── types/
│ └── index.tsx
├── components/ # Shared components
├── hooks/ # Shared hooks
└── types/ # Shared typesComponent Co-location
组件就近放置
features/posts/
├── components/
│ ├── PostCard.tsx
│ ├── PostList.tsx
│ └── PostForm.tsx
├── hooks/
│ ├── usePost.ts
│ └── usePosts.ts
├── types/
│ └── post.ts
└── index.tsx # Public APIfeatures/posts/
├── components/
│ ├── PostCard.tsx
│ ├── PostList.tsx
│ └── PostForm.tsx
├── hooks/
│ ├── usePost.ts
│ └── usePosts.ts
├── types/
│ └── post.ts
└── index.tsx # Public APICommon Mistakes to Avoid
需避免的常见错误
1. Conditional Hooks
1. 条件式调用Hooks
typescript
// ❌ Never do this
function Component({ condition }) {
if (condition) {
const [state, setState] = useState(0); // Breaks rules of hooks
}
}
// ✅ Do this
function Component({ condition }) {
const [state, setState] = useState(0);
// Use state conditionally, not the hook
}typescript
// ❌ Never do this
function Component({ condition }) {
if (condition) {
const [state, setState] = useState(0); // Breaks rules of hooks
}
}
// ✅ Do this
function Component({ condition }) {
const [state, setState] = useState(0);
// Use state conditionally, not the hook
}2. Missing Dependencies
2. 缺失依赖项
typescript
// ❌ Bad: Missing dependency
useEffect(() => {
fetchUser(userId);
}, []); // userId should be in deps
// ✅ Good: All dependencies listed
useEffect(() => {
fetchUser(userId);
}, [userId]);typescript
// ❌ Bad: Missing dependency
useEffect(() => {
fetchUser(userId);
}, []); // userId should be in deps
// ✅ Good: All dependencies listed
useEffect(() => {
fetchUser(userId);
}, [userId]);3. Mutating State
3. 直接修改状态
typescript
// ❌ Bad: Mutating state directly
const handleAdd = () => {
items.push(newItem); // Don't mutate
setItems(items);
};
// ✅ Good: Create new array
const handleAdd = () => {
setItems([...items, newItem]);
};typescript
// ❌ Bad: Mutating state directly
const handleAdd = () => {
items.push(newItem); // Don't mutate
setItems(items);
};
// ✅ Good: Create new array
const handleAdd = () => {
setItems([...items, newItem]);
};Additional Resources
额外资源
For more detailed patterns, see:
- component-patterns.md - Advanced component patterns
- performance.md - Performance optimization techniques
- typescript-patterns.md - TypeScript best practices
- hooks-patterns.md - Custom hooks and advanced patterns
如需更详细的模式,请参考:
- component-patterns.md - 高级组件模式
- performance.md - 性能优化技巧
- typescript-patterns.md - TypeScript最佳实践
- hooks-patterns.md - 自定义Hooks及高级模式