关于 react-patterns
react-patterns 是面向 React 开发者的技能指南,涵盖现代 Hooks、组件组合、状态管理与性能实践。
主要功能
- 01React Hooks 与状态模式
- 02自定义 Hook 设计
- 03组件组合方式
- 04性能优化
- 05服务端与客户端组件
适用场景
- 01为复杂界面设计状态
- 02提取可复用的组件逻辑
- 03减少不必要的重复渲染
- 04重构 class 组件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ manutej/ luxor-claude-marketplace --skill react-patterns安装后在 Claude Code 中输入/react-patterns确认已加载。
附带文件EXAMPLES.mdREADME.md
详细介绍
React 模式 - 现代开发指南
全面掌握现代 React 开发模式的技能,涵盖 React 18+ 功能、Hooks、组件组合、状态管理策略,以及用于构建可扩展 Web 应用的性能优化技巧。
何时使用此技能
在以下情况下使用此技能:
- 使用函数组件和 Hooks 构建现代 React 应用
- 使用 useReducer 和 Context API 实现复杂的状态管理
- 使用记忆化技术优化 React 应用性能
- 创建可复用的自定义 Hooks,以共享逻辑
- 使用 Server Components 和 React Server Components (RSC)
- 管理表单、副作用和异步操作
- 将类组件重构为现代函数式模式
- 使用恰当的模式构建类型安全的 React 应用
- 实现高级组件组合模式
- 调试 React 性能问题和不必要的重新渲染
核心概念
React 理念
现代 React 强调:
- 函数组件:返回 JSX 的纯函数
- Hooks:可组合的状态和副作用管理
- 声明式 UI:描述 UI 应呈现的样子,由 React 处理更新
- 组件组合:使用简单、可复用的组件构建复杂 UI
- 单向数据流:Props 向下传递,事件向上传递
- 不可变性:绝不直接修改状态,始终创建新对象/数组
组件类型
- 展示组件:专注于 UI,通过 props 接收数据
- 容器组件:处理逻辑、状态和副作用
- 服务端组件:在服务端渲染,不包含客户端 JavaScript
- 客户端组件:使用
"use client"标记的交互式组件 - 异步组件:可以等待数据的服务端组件
React Hooks 参考
内置状态 Hooks
useState
管理简单值的本地组件状态。
语法:
const [state, setState] = useState(initialValue);
基本示例:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(prev => prev + 1)}>Increment (functional)</button>
</div>
);
}
最佳实践:
- 当新状态取决于先前状态时,使用函数式更新
- 尽可能将状态保留在本地
- 不要将派生值存储在状态中
- 对于开销较大的计算,使用函数进行初始化
多个状态变量:
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(0);
return (
<form>
<input value={name} onChange={e => setName(e.target.value)} />
<input value={email} onChange={e => setEmail(e.target.value)} />
<input type="number" value={age} onChange={e => setAge(Number(e.target.value))} />
</form>
);
}
惰性初始化:
function ExpensiveComponent() {
// Function only runs once on initial render
const [state, setState] = useState(() => {
const initialValue = expensiveComputation();
return initialValue;
});
return <div>{state}</div>;
}
useReducer
使用 reducer 模式管理复杂状态逻辑(类似于 Redux)。
语法:
const [state, dispatch] = useReducer(reducer, initialState, init?);
任务管理器示例:
import { useReducer } from 'react';
function tasksReducer(tasks, action) {
switch (action.type) {
case 'added':
return [...tasks, {
id: action.id,
text: action.text,
done: false
}];
case 'changed':
return tasks.map(t =>
t.id === action.task.id ? action.task : t
);
case 'deleted':
return tasks.filter(t => t.id !== action.id);
default:
throw Error('Unknown action: ' + action.type);
}
}
function TaskApp() {
const [tasks, dispatch] = useReducer(tasksReducer, []);
function handleAddTask(text) {
dispatch({
type: 'added',
id: Date.now(),
text: text,
});
}
function handleChangeTask(task) {
dispatch({
type: 'changed',
task: task
});
}
function handleDeleteTask(taskId) {
dispatch({
type: 'deleted',
id: taskId
});
}
return (
<>
<AddTask onAddTask={handleAddTask} />
<TaskList
tasks={tasks}
onChangeTask={handleChangeTask}
onDeleteTask={handleDeleteTask}
/>
</>
);
}
何时使用 useReducer:
- 存在多个相关状态值
- 状态更新逻辑复杂
- 状态转换遵循可预测的模式
- 需要优化包含大量更新的性能
- 希望将状态逻辑与组件分离
useContext
访问上下文值,避免逐层传递 props。
语法:
const value = useContext(SomeContext);
主题上下文示例:
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext(null);
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// Usage
function Button() {
const { theme, toggleTheme } = useTheme();
return (
<button
onClick={toggleTheme}
className={theme === 'dark' ? 'btn-dark' : 'btn-light'}
>
Toggle Theme
</button>
);
}
组合使用 useReducer + useContext:
import { createContext, useContext, useReducer } from 'react';
const TasksContext = createContext(null);
const TasksDispatchContext = createContext(null);
export function TasksProvider({ children }) {
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
return (
<TasksContext.Provider value={tasks}>
<TasksDispatchContext.Provider value={dispatch}>
{children}
</TasksDispatchContext.Provider>
</TasksContext.Provider>
);
}
export function useTasks() {
return useContext(TasksContext);
}
export function useTasksDispatch() {
return useContext(TasksDispatchContext);
}
// Usage in components
function TaskList() {
const tasks = useTasks();
const dispatch = useTasksDispatch();
return (
<ul>
{tasks.map(task => (
<li key={task.id}>
{task.text}
<button onClick={() => dispatch({ type: 'deleted', id: task.id })}>
Delete
</button>
</li>
))}
</ul>
);
}
Effect Hooks
useEffect
将组件与外部系统(API、DOM、订阅)同步。
语法:
useEffect(() => {
// Effect logic
return () => {
// Cleanup logic
};
}, [dependencies]);
数据获取示例:
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let ignore = false; // Prevent race conditions
async function fetchUser() {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
if (!ignore) {
setUser(data);
}
} catch (err) {
if (!ignore) {
setError(err.message);
}
} finally {
if (!ignore) {
setLoading(false);
}
}
}
fetchUser();
return () => {
ignore = true; // Cleanup
};
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>User: {user?.name}</div>;
}
订阅示例:
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const connection = createConnection(roomId);
connection.on('message', (msg) => {
setMessages(prev => [...prev, msg]);
});
connection.connect();
return () => {
connection.disconnect(); // Cleanup on unmount or roomId change
};
}, [roomId]);
return (
<div>
{messages.map((msg, i) => (
<div key={i}>{msg}</div>
))}
</div>
);
}
常见 Effect 模式:
// Run once on mount
useEffect(() => {
console.log('Component mounted');
}, []); // Empty dependency array
// Run on every render
useEffect(() => {
console.log('Component rendered');
}); // No dependency array
// Run when specific values change
useEffect(() => {
console.log('userId changed:', userId);
}, [userId]); // Dependency array with values
// Cleanup on unmount
useEffect(() => {
const timer = setTimeout(() => {
console.log('Delayed action');
}, 1000);
return () => clearTimeout(timer);
}, []);
useLayoutEffect
useEffect 的同步版本,在浏览器绘制前运行。
使用场景:
- 测量 DOM 元素
- 同步 DOM 修改
- 防止视觉闪烁
示例:
import { useLayoutEffect, useRef, useState } from 'react';
function Tooltip() {
const ref = useRef(null);
const [tooltipHeight, setTooltipHeight] = useState(0);
useLayoutEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setTooltipHeight(height); // Synchronous update before paint
}, []);
return (
<div ref={ref}>
Tooltip content (height: {tooltipHeight}px)
</div>
);
}
性能 Hooks
useMemo
在多次渲染之间缓存开销较大的计算结果。
语法:
const cachedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
示例:
import { useMemo } from 'react';
function TodoList({ todos, filter }) {
// Only recompute when todos or filter changes
const visibleTodos = useMemo(() => {
console.log('Filtering todos...');
return todos.filter(todo => {
if (filter === 'all') return true;
if (filter === 'active') return !todo.done;
if (filter === 'completed') return todo.done;
return true;
});
}, [todos, filter]);
return (
<ul>
{visibleTodos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
何时使用 useMemo:
- 开销较大的计算(筛选、排序大型数组)
- 防止子组件不必要的重新渲染
- 稳定作为依赖项传递的对象/数组引用
反模式(不必要的用法):
// ❌ Don't use for simple calculations
const sum = useMemo(() => a + b, [a, b]); // Overkill
// ✅ Just compute directly
const sum = a + b;
useCallback
在多次渲染之间记忆化函数引用。
语法:
const cachedFn = useCallback(() => {
// Function logic
}, [dependencies]);
示例:
import { useState, useCallback, memo } from 'react';
const TodoItem = memo(function TodoItem({ todo, onChange, onDelete }) {
console.log('TodoItem rendered:', todo.id);
return (
<li>
<input
type="checkbox"
checked={todo.done}
onChange={() => onChange(todo)}
/>
{todo.text}
<button onClick={() => onDelete(todo.id)}>Delete</button>
</li>
);
});
function TodoList() {
const [todos, setTodos] = useState([]);
// Memoize handlers to prevent TodoItem re-renders
const handleChange = useCallback((todo) => {
setTodos(prev => prev.map(t =>
t.id === todo.id ? { ...t, done: !t.done } : t
));
}, []);
const handleDelete = useCallback((id) => {
setTodos(prev => prev.filter(t => t.id !== id));
}, []);
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onChange={handleChange}
onDelete={handleDelete}
/>
))}
</ul>
);
}
何时使用 useCallback:
- 将回调传递给经过记忆化的子组件
- 函数是 useEffect 或其他 Hooks 的依赖项
- 优化开销较大的事件处理器
React.memo
对整个组件进行记忆化,避免不必要的重新渲染。
语法:
const MemoizedComponent = memo(function Component(props) {
// Component logic
}, arePropsEqual?);
示例:
import { memo } from 'react';
const ExpensiveComponent = memo(function ExpensiveComponent({ data, onClick }) {
console.log('ExpensiveComponent rendered');
return (
<div>
{data.map(item => (
<div key={item.id} onClick={() => onClick(item)}>
{item.name}
</div>
))}
</div>
);
});
// With custom comparison
const CustomMemoComponent = memo(
function CustomMemoComponent({ user }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
// Return true if props are equal (skip re-render)
return prevProps.user.id === nextProps.user.id;
}
);
Ref Hook
useRef
创建可变引用,并使其在多次渲染之间保持不变。
语法:
const ref = useRef(initialValue);
DOM 引用示例:
import { useRef } from 'react';
function VideoPlayer({ src, isPlaying }) {
const videoRef = useRef(null);
useEffect(() => {
if (isPlaying) {
videoRef.current.play();
} else {
videoRef.current.pause();
}
}, [isPlaying]);
return <video ref={videoRef} src={src} />;
}
存储可变值:
function Timer() {
const intervalRef = useRef(null);
const [count, setCount] = useState(0);
function start() {
if (intervalRef.current) return; // Already running
intervalRef.current = setInterval(() => {
setCount(c => c + 1);
}, 1000);
}
function stop() {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
}
return (
<div>
<p>Count: {count}</p>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</div>
);
}
上一个值模式:
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
// Usage
function Counter({ count }) {
const prevCount = usePrevious(count);
return (
<div>
<p>Current: {count}</p>
<p>Previous: {prevCount}</p>
</div>
);
}
Transition Hook(React 18+)
useTransition
将状态更新标记为非紧急更新,让 UI 保持响应。
语法:
const [isPending, startTransition] = useTransition();
示例:
import { useState, useTransition } from 'react';
function SearchResults() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
function handleChange(e) {
const value = e.target.value;
setQuery(value); // Urgent update (input value)
startTransition(() => {
// Non-urgent update (search results)
const filtered = performExpensiveSearch(value);
setResults(filtered);
});
}
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <div>Searching...</div>}
<ul>
{results.map(result => (
<li key={result.id}>{result.name}</li>
))}
</ul>
</div>
);
}
useDeferredValue
延迟更新部分 UI。
语法:
const deferredValue = useDeferredValue(value);
示例:
import { useState, useDeferredValue, memo } from 'react';
const SlowList = memo(function SlowList({ items }) {
// Intentionally slow rendering
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
});
function App() {
const [text, setText] = useState('');
const deferredText = useDeferredValue(text);
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<SlowList items={filterItems(deferredText)} />
</>
);
}
Server Action Hook(RSC)
useActionState
管理 server action 的状态和待处理状态。
语法:
const [state, formAction, isPending] = useActionState(serverAction, initialState);
示例:
"use client";
import { useActionState } from 'react';
import { updateName } from './actions';
function UpdateNameForm() {
const [state, submitAction, isPending] = useActionState(
updateName,
{ error: null }
);
return (
<form action={submitAction}>
<input type="text" name="name" disabled={isPending} />
{state.error && <span className="error">{state.error}</span>}
<button type="submit" disabled={isPending}>
{isPending ? 'Updating...' : 'Update'}
</button>
</form>
);
}
Server Action(actions.js):
"use server";
export async function updateName(prevState, formData) {
const name = formData.get('name');
if (!name) {
return { error: 'Name is required' };
}
try {
await db.users.updateName(name);
return { error: null };
} catch (err) {
return { error: 'Failed to update name' };
}
}
自定义 Hook 模式
自定义 Hook 可让你提取有状态逻辑,并在多个组件之间复用。
命名约定
自定义 Hook 的名称始终以 use 为前缀:
useCustomHook // ✅ Correct
customHook // ❌ Wrong
数据获取 Hook
import { useState, useEffect } from 'react';
export function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let ignore = false;
async function fetchData() {
try {
setLoading(true);
setError(null);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (!ignore) {
setData(result);
}
} catch (err) {
if (!ignore) {
setError(err.message);
}
} finally {
if (!ignore) {
setLoading(false);
}
}
}
fetchData();
return () => {
ignore = true;
};
}, [url]);
return { data, loading, error };
}
// Usage
function UserProfile({ userId }) {
const { data: user, loading, error } = useFetch(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>{user.name}</div>;
}
表单输入 Hook
import { useState } from 'react';
export function useFormInput(initialValue = '') {
const [value, setValue] = useState(initialValue);
function handleChange(e) {
setValue(e.target.value);
}
function reset() {
setValue(initialValue);
}
return {
value,
onChange: handleChange,
reset
};
}
// Usage
function LoginForm() {
const email = useFormInput('');
const password = useFormInput('');
function handleSubmit(e) {
e.preventDefault();
console.log('Email:', email.value);
console.log('Password:', password.value);
email.reset();
password.reset();
}
return (
<form onSubmit={handleSubmit}>
<input type="email" {...email} placeholder="Email" />
<input type="password" {...password} placeholder="Password" />
<button type="submit">Login</button>
</form>
);
}
本地存储 Hook
import { useState, useEffect } from 'react';
export function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
useEffect(() => {
try {
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(error);
}
}, [key, value]);
return [value, setValue];
}
// Usage
function Settings() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
const [language, setLanguage] = useLocalStorage('language', 'en');
return (
<div>
<select value={theme} onChange={e => setTheme(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
<select value={language} onChange={e => setLanguage(e.target.value)}>
<option value="en">English</option>
<option value="es">Spanish</option>
</select>
</div>
);
}
防抖 Hook
import { useState, useEffect } from 'react';
export function useDebounce(value, delay = 500) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
// Usage
function SearchComponent() {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearchTerm = useDebounce(searchTerm, 500);
useEffect(() => {
if (debouncedSearchTerm) {
// Perform search
console.log('Searching for:', debouncedSearchTerm);
}
}, [debouncedSearchTerm]);
return (
<input
type="text"
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
placeholder="Search..."
/>
);
}
窗口大小 Hook
import { useState, useEffect } from 'react';
export function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: undefined,
height: undefined,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
handleResize(); // Set initial size
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return windowSize;
}
// Usage
function ResponsiveComponent() {
const { width, height } = useWindowSize();
return (
<div>
<p>Window width: {width}px</p>
<p>Window height: {height}px</p>
{width < 768 ? <MobileView /> : <DesktopView />}
</div>
);
}
在线状态 Hook
import { useState, useEffect } from 'react';
export function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}
// Usage
function StatusBar() {
const isOnline = useOnlineStatus();
return (
<div className={isOnline ? 'online' : 'offline'}>
{isOnline ? '✅ Online' : '❌ Offline'}
</div>
);
}
组件模式
复合组件
构建能够协同工作并共享隐式状态的组件。
import { createContext, useContext, useState } from 'react';
const TabsContext = createContext();
export function Tabs({ children, defaultValue }) {
const [activeTab, setActiveTab] = useState(defaultValue);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
export function TabList({ children }) {
return <div className="tab-list">{children}</div>;
}
export function Tab({ value, children }) {
const { activeTab, setActiveTab } = useContext(TabsContext);
const isActive = activeTab === value;
return (
<button
className={`tab ${isActive ? 'active' : ''}`}
onClick={() => setActiveTab(value)}
>
{children}
</button>
);
}
export function TabPanels({ children }) {
return <div className="tab-panels">{children}</div>;
}
export function TabPanel({ value, children }) {
const { activeTab } = useContext(TabsContext);
if (activeTab !== value) return null;
return <div className="tab-panel">{children}</div>;
}
// Usage
function App() {
return (
<Tabs defaultValue="tab1">
<TabList>
<Tab value="tab1">Tab 1</Tab>
<Tab value="tab2">Tab 2</Tab>
<Tab value="tab3">Tab 3</Tab>
</TabList>
<TabPanels>
<TabPanel value="tab1">Content for Tab 1</TabPanel>
<TabPanel value="tab2">Content for Tab 2</TabPanel>
<TabPanel value="tab3">Content for Tab 3</TabPanel>
</TabPanels>
</Tabs>
);
}
Render Props
使用值为函数的 prop 在组件之间共享代码。
function DataFetcher({ url, render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]);
return render({ data, loading });
}
// Usage
function App() {
return (
<DataFetcher
url="/api/users"
render={({ data, loading }) => (
loading ? <div>Loading...</div> : (
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
)}
/>
);
}
高阶组件(HOC)
包装组件以增强功能。
import { useEffect, useState } from 'react';
// HOC for data fetching
function withData(Component, url) {
return function WithDataComponent(props) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, []);
return <Component {...props} data={data} loading={loading} />;
};
}
// Original component
function UserList({ data, loading }) {
if (loading) return <div>Loading...</div>;
return (
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
// Enhanced component
const UserListWithData = withData(UserList, '/api/users');
// Usage
function App() {
return <UserListWithData />;
}
容器/展示模式
将逻辑与展示分离。
// Container (logic)
function UserListContainer() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
fetchUsers().then(data => {
setUsers(data);
setLoading(false);
});
}, []);
const filteredUsers = users.filter(user =>
user.name.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<UserListPresenter
users={filteredUsers}
loading={loading}
searchTerm={searchTerm}
onSearchChange={setSearchTerm}
/>
);
}
// Presenter (UI)
function UserListPresenter({ users, loading, searchTerm, onSearchChange }) {
if (loading) return <div>Loading...</div>;
return (
<div>
<input
type="text"
value={searchTerm}
onChange={e => onSearchChange(e.target.value)}
placeholder="Search users..."
/>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
性能优化
识别性能问题
使用 React DevTools Profiler 识别:
- 不必要地重新渲染的组件
- 开销较大的渲染操作
- 状态更新级联
优化策略
1. 记忆化
import { memo, useMemo, useCallback } from 'react';
const ExpensiveList = memo(function ExpensiveList({ items, onItemClick }) {
console.log('Rendering ExpensiveList');
return (
<ul>
{items.map(item => (
<li key={item.id} onClick={() => onItemClick(item.id)}>
{item.name}
</li>
))}
</ul>
);
});
function App() {
const [items, setItems] = useState([]);
const [count, setCount] = useState(0);
// Memoize expensive computation
const sortedItems = useMemo(() => {
console.log('Sorting items...');
return [...items].sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
// Memoize callback
const handleItemClick = useCallback((id) => {
console.log('Item clicked:', id);
}, []);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
<ExpensiveList items={sortedItems} onItemClick={handleItemClick} />
</div>
);
}
2. 代码拆分
import { lazy, Suspense } from 'react';
// Lazy load components
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const Dashboard = lazy(() => import('./Dashboard'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
// Route-based splitting
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
3. 虚拟化
import { FixedSizeList } from 'react-window';
function VirtualizedList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>
{items[index].name}
</div>
);
return (
<FixedSizeList
height={500}
itemCount={items.length}
itemSize={50}
width="100%"
>
{Row}
</FixedSizeList>
);
}
4. 避免匿名函数
// ❌ Bad - creates new function on every render
function List({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id} onClick={() => console.log(item.id)}>
{item.name}
</li>
))}
</ul>
);
}
// ✅ Good - use useCallback
function List({ items }) {
const handleClick = useCallback((id) => {
console.log(id);
}, []);
return (
<ul>
{items.map(item => (
<li key={item.id} onClick={() => handleClick(item.id)}>
{item.name}
</li>
))}
</ul>
);
}
Server Components(RSC)
React Server Components 在服务端渲染,可减小客户端 bundle 的体积。
Server Component
// app/users/page.js (Server Component by default)
async function UsersPage() {
// Fetch data directly on server
const users = await db.users.findAll();
return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
export default UsersPage;
Client Component
// components/Counter.js
"use client"; // Mark as client component
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}
组合模式
// app/page.js (Server Component)
import Counter from '@/components/Counter'; // Client Component
async function HomePage() {
const data = await fetchData(); // Server-side data fetching
return (
<div>
<h1>{data.title}</h1>
<p>{data.description}</p>
<Counter /> {/* Interactive client component */}
</div>
);
}
export default HomePage;
状态管理策略
本地状态(useState)
适用于:组件特定状态
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
提升状态
适用于:兄弟组件之间共享状态
function Parent() {
const [sharedValue, setSharedValue] = useState('');
return (
<>
<ChildA value={sharedValue} onChange={setSharedValue} />
<ChildB value={sharedValue} />
</>
);
}
Context API
适用于:整个应用范围的状态(主题、身份验证、语言)
const AuthContext = createContext();
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = async (credentials) => {
const user = await api.login(credentials);
setUser(user);
};
const logout = () => setUser(null);
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
useReducer + Context
适用于:包含多个操作的复杂状态逻辑
const StateContext = createContext();
const DispatchContext = createContext();
function reducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
return { ...state, items: [...state.items, action.payload] };
case 'REMOVE_ITEM':
return { ...state, items: state.items.filter(i => i.id !== action.payload) };
default:
return state;
}
}
function StoreProvider({ children }) {
const [state, dispatch] = useReducer(reducer, { items: [] });
return (
<StateContext.Provider value={state}>
<DispatchContext.Provider value={dispatch}>
{children}
</DispatchContext.Provider>
</StateContext.Provider>
);
}
最佳实践
1. 组件设计
- 保持组件小巧且专注(单一职责)
- 将可复用逻辑提取到自定义 Hook 中
- 优先使用组合,而非继承
- 谨慎使用 prop 展开
- 定义 PropTypes 或 TypeScript 类型
2. 状态管理
- 尽可能让状态保持在本地
- 仅在必要时提升状态
- 避免不必要的状态(尽可能从 props 派生)
- 对复杂状态逻辑使用 reducer
- 尽可能批量更新状态
3. 性能
- 不要过早优化——先进行测量
- 使用 React DevTools Profiler
- 对开销较大的计算进行记忆化
- 避免在渲染期间创建对象/数组
- 为列表正确使用 key prop
4. Effects
- 让 effect 保持专注(每个 effect 处理一个关注点)
- 始终指定依赖项
- 清理 effect(返回清理函数)
- 对 fetch 请求使用 AbortController
- 使用 ignore 标志避免竞态条件
5. 代码组织
src/
├── components/
│ ├── common/ # Reusable UI components
│ ├── features/ # Feature-specific components
│ └── layout/ # Layout components
├── hooks/ # Custom hooks
├── context/ # Context providers
├── utils/ # Utility functions
├── services/ # API services
└── types/ # TypeScript types
应避免的常见反模式
1. 修改状态
// ❌ Wrong
const handleAdd = () => {
items.push(newItem);
setItems(items);
};
// ✅ Correct
const handleAdd = () => {
setItems([...items, newItem]);
};
2. 缺少依赖项
// ❌ Wrong
useEffect(() => {
console.log(userId);
}, []); // Missing userId dependency
// ✅ Correct
useEffect(() => {
console.log(userId);
}, [userId]);
3. 条件调用 Hook
// ❌ Wrong
if (condition) {
const [state, setState] = useState(0);
}
// ✅ Correct
const [state, setState] = useState(0);
if (condition) {
// Use state
}
4. 在组件内部创建组件
// ❌ Wrong
function Parent() {
function Child() { // Re-created on every render
return <div>Child</div>;
}
return <Child />;
}
// ✅ Correct
function Child() {
return <div>Child</div>;
}
function Parent() {
return <Child />;
}
故障排除
常见问题
问题:无限重新渲染循环
- 检查 useEffect 依赖项
- 避免在渲染期间直接设置状态
- 使用函数式更新:
setState(prev => prev + 1)
问题:闭包中的值已过期
- 在 setState 中使用函数式更新
- 在 useEffect 中添加缺失的依赖项
- 对可变值使用 useRef
问题:组件未重新渲染
- 检查状态是否被修改(使用不可变更新)
- 验证 React.memo 比较函数
- 确保对象/数组具有新的引用
问题:内存泄漏警告
- 清理副作用(返回清理函数)
- 在卸载时取消正在进行的请求
- 清除计时器/间隔
资源
- React 文档:https://react.dev
- React Hooks:https://react.dev/reference/react
- React 服务端组件:https://react.dev/reference/rsc
- React DevTools:https://react.dev/learn/react-developer-tools
- 模式:https://patterns.dev/react
技能版本:1.0.0 最后更新:2025 年 10 月 技能类别:前端开发、React、JavaScript 兼容版本:React 18+、Next.js 13+、TypeScript