在 React 中应用设计模式可以提高代码可维护性、复用性和可扩展性,以下总结一些设计模式在React中的应用。
1、 组件组合模式 (Component Composition)
- 核心思想:通过 props 组合组件,避免复杂的继承结构
- 优势:解耦容器与内容,增强组件复用性
// 父组件使用children prop组合
const Card = ({ title, children }) => (
<div className="card">
<h2>{title}</h2>
<div className="content">{children}</div>
</div>
);
// 使用
<Card title="用户信息">
<Avatar />
<UserDetails />
</Card>
2、高阶组件 (HOC)
- 核心思想:通过函数返回新的组件,实现对组件功能的扩展
- 优势:复用组件逻辑,减少重复代码,提高组件可维护性
- 场景:Redux 的 connect、React Router 的 withRouter
const withLogger = (WrappedComponent) => {
return (props) => {
useEffect(() => {
console.log(`${WrappedComponent.name} mounted`);
}, []);
return <WrappedComponent {...props} />;
};
};
// 使用
const EnhancedButton = withLogger(Button);
<EnhancedButton />
3、渲染属性 (Render Props)
- 核心思想:通过函数 prop 共享组件逻辑,实现组件的解耦
- 优势:比 HOC 更灵活,避免组件嵌套地狱
<MouseTracker>
{({ x, y }) => (
<div>
鼠标位置: {x}, {y}
</div>
)}
</MouseTracker>
// 实现
const MouseTracker = ({ children }) => {
const [pos, setPos] = useState({ x: 0, y: 0 });
// ...监听鼠标移动逻辑
return children(pos); // 关键:调用函数prop
};
4、 提供者模式 (Provider Pattern)
- 核心思想:使用 Context API 跨层级传递数据
- 优势:避免了 props drilling,使组件树更清晰
- 场景:主题切换、用户身份、多语言
const ThemeContext = React.createContext();
// 顶层提供数据
<ThemeContext.Provider value="dark">
<App />
</ThemeContext.Provider>
// 子组件消费
const Button = () => {
const theme = useContext(ThemeContext);
return <button className={`btn-${theme}`}>按钮</button>;
};
5、状态机模式 (State Machine)
- 核心思想:使用状态机管理复杂状态流转
import { useReducer } from 'react';
const reducer = (state, action) => {
switch (state.status) {
case 'idle':
return action.type === 'FETCH' ? { status: 'loading' } : state;
case 'loading':
return action.type === 'SUCCESS'
? { status: 'success', data: action.payload }
: { status: 'error', error: action.error };
default:
return state;
}
};
// 组件内使用
const [state, dispatch] = useReducer(reducer, { status: 'idle' });
6、复合组件 (Compound Components)
- 核心思想:关联组件共享隐式状态
- 优势:简化 API 设计
// 定义
const Tabs = ({ children }) => {
const [activeIndex, setActiveIndex] = useState(0);
return React.Children.map(children, (child, index) =>
React.cloneElement(child, {
isActive: index === activeIndex,
onClick: () => setActiveIndex(index)
})
);
};
// 子组件无需单独传递状态
const TabItem = ({ isActive, onClick, children }) => (
<div
className={`tab ${isActive ? 'active' : ''}`}
onClick={onClick}
>
{children}
</div>
);
// 使用
<Tabs>
<TabItem>标签1</TabItem>
<TabItem>标签2</TabItem>
</Tabs>
7、观察者模式 & 发布订阅
- 核心思想:通过事件监听机制实现数据更新通知
- 优势:解耦组件间通信,提高组件可维护性
// 创建事件总线
const EventBus = {
listeners: {},
emit(event, data) {
(this.listeners[event] || []).forEach(fn => fn(data));
},
on(event, callback) {
this.listeners[event] = [...(this.listeners[event] || []), callback];
},
off(event, callback) {
this.listeners[event] = this.listeners[event].filter(fn => fn !== callback);
}
};
// 组件A触发事件
EventBus.emit('dataUpdate', { newData: 123 });
// 组件B监听
useEffect(() => {
const handler = data => console.log(data);
EventBus.on('dataUpdate', handler);
return () => EventBus.off('dataUpdate', handler); // 清理
}, []);
8、策略模式
- 核心思想:预设策略集,通过传入参数选择执行策略
// 策略集
const formatters = {
date: (value) => new Date(value).toLocaleDateString(),
currency: (value) => `$${parseFloat(value).toFixed(2)}`,
default: (value) => value
};
// 使用策略
const FormattedText = ({ type, value }) => (
<span>{formatters[type]?.(value) || formatters.default(value)}</span>
);
// 调用
<FormattedText type="currency" value="9.99" />
9、懒加载模式 (Lazy Initialization)
- 核心思想:在需要时才初始化对象或资源
- 优势:减少初始化开销,提高性能
import React, { lazy, Suspense } from 'react';
const LazyDashboard = lazy(() => import('./Dashboard'));
const App = () => (
<Suspense fallback={<div>加载中...</div>}>
{userLoggedIn && <LazyDashboard />}
</Suspense>
);
10、享元模式 (Flyweight)
- 核心思想:复用对象,减少内存占用
- 优势:提高性能,减少内存占用
// 共享图标组件
const icons = {
star: <StarIcon />,
heart: <HeartIcon />,
// ...
};
const Icon = ({ type }) => {
return icons[type] || <DefaultIcon />;
};
// 复用相同实例
<Icon type="star" />
<Icon type="star" /> // 复用相同内存实例
11、原型模式 (Prototype)
- 核心思想:通过Object.create()创建新对象
const defaultFormConfig = {
validate: () => true,
style: { padding: 10 },
// ...
};
// 基于原型创建新表单
const LoginForm = () => {
const formConfig = Object.create(defaultFormConfig);
formConfig.fields = [/*...*/];
return <Form config={formConfig} />;
};
12、职责链模式 (Chain of Responsibility)
- 核心思想:多个对象依次处理请求
const errorHandlers = [
(error) => isNetworkError(error) ? handleNetworkError(error) : null,
(error) => isAuthError(error) ? handleAuthError(error) : null,
(error) => handleGenericError(error) // 兜底处理
];
const processError = (error) => {
for (const handler of errorHandlers) {
const result = handler(error);
if (result !== null) return result;
}
};
// 组件中使用
try { /* ... */ }
catch (error) { processError(error); }
13、中介者模式 (Mediator)
- 核心思想:通过中介者对象管理组件之间的通信
class ChatMediator {
participants = [];
register(participant) {
this.participants.push(participant);
}
send(message, sender) {
this.participants
.filter(p => p !== sender)
.forEach(p => p.receive(message));
}
}
// 组件中
const ChatUser = ({ name, mediator }) => {
useEffect(() => {
mediator.register({ receive: handleMessage, name });
}, []);
const send = () => mediator.send(text, name);
};
14、 空对象模式 (Null Object)
- 核心思想:提供默认对象避免空值检查
const NullComponent = () => null;
const getUserComponent = (user) => {
if (!user) return NullComponent;
if (user.type === 'admin') return AdminPanel;
return UserPanel;
};
const UserSection = ({ user }) => {
const UserComponent = getUserComponent(user);
return <UserComponent />; // 无需检查空值
};
15、访问者模式 (Visitor)
- 核心思想:定义新操作而不修改元素类
- 优势:扩展性高,避免了修改元素类
// 数据形状
const elements = [
{ type: 'text', content: 'Hello' },
{ type: 'image', src: 'logo.png' }
];
// 访问器实现
const renderers = {
text: (element) => <Text content={element.content} />,
image: (element) => <Image src={element.src} />,
default: () => <FallbackComponent />
};
// 渲染器
const Renderer = ({ elements }) => (
<>
{elements.map(element => {
const render = renderers[element.type] || renderers.default;
return render(element);
})}
</>
);
16、命令模式 (Command)
- 核心思想:将请求封装为对象,实现请求的排队、记录、撤销和重做功能
- 优势:解耦命令与执行者,提高代码可读性
import React, { useState } from 'react';
class CommandManager {
constructor() {
this.history = [];
this.position = -1;
}
execute(command) {
// 清除当前位置之后的历史
if (this.position < this.history.length - 1) {
this.history = this.history.slice(0, this.position + 1);
}
command.execute();
this.history.push(command);
this.position = this.history.length - 1;
}
undo() {
if (this.position >= 0) {
this.history[this.position].undo();
this.position--;
}
}
redo() {
if (this.position < this.history.length - 1) {
this.position++;
this.history[this.position].execute();
}
}
get canUndo() {
return this.position >= 0;
}
get canRedo() {
return this.position < this.history.length - 1;
}
}
// 命令基类
class Command {
constructor(execute, undo, data) {
this.execute = () => execute(data);
this.undo = () => undo(data);
}
}
const UndoableCounter = () => {
const [count, setCount] = useState(0);
const [commandManager] = useState(() => new CommandManager());
// 执行命令函数
const executeCommand = (action, value) => {
const newValue = action === 'increment' ? count + value : count - value;
const command = new Command(
(data) => setCount(data.newValue),
(data) => setCount(data.oldValue),
{
oldValue: count,
newValue,
action,
value
}
);
commandManager.execute(command);
};
// 操作按钮
const ActionButton = ({ action, value, children }) => (
<button onClick={() => executeCommand(action, value)}>
{children}
</button>
);
return (
<div className="undoable-counter">
<h2>可撤销计数器</h2>
<div className="counter-display">{count}</div>
<div className="controls">
<ActionButton action="increment" value={1}>
+1
</ActionButton>
<ActionButton action="increment" value={5}>
+5
</ActionButton>
<ActionButton action="decrement" value={1}>
-1
</ActionButton>
<ActionButton action="decrement" value={5}>
-5
</ActionButton>
<button
onClick={commandManager.undo.bind(commandManager)}
disabled={!commandManager.canUndo}
className="undo-btn"
>
撤销
</button>
<button
onClick={commandManager.redo.bind(commandManager)}
disabled={!commandManager.canRedo}
className="redo-btn"
>
重做
</button>
</div>
<div className="command-history">
<h3>操作历史:</h3>
<ul>
{commandManager.history.map((command, index) => (
<li
key={index}
className={index === commandManager.position ? 'current' : ''}
>
{command.data.action} {command.data.value}
</li>
))}
</ul>
</div>
</div>
);
};
export default UndoableCounter;
17、迭代器模式 (Iterator)
- 核心思想:提供一致的数据遍历接口
- 优势:隐藏聚合对象的内部结构,提高代码可读性
function* paginatedIterator(fetchPage) {
let page = 0;
let hasMore = true;
while (hasMore) {
// 等待获取一页数据
const response = yield fetchPage(page);
// 更新下一页和是否还有更多数据
hasMore = response.hasMore;
page += 1;
}
};
// 组件中使用
useEffect(() => {
// 初始化生成器
const paginator = paginatedFetcher(fetchPage);
// 启动生成器,获取第一个yield返回的值(即第一个fetchPage(0))
const { value } = paginator.next();
setUsers(prev => [...prev, ...value]);
}, []);
18、依赖注入模式 (Dependency Injection)
- 核心思想:通过上下文或Props注入服务依赖,以解耦组件
- 优势:提高代码可测试性,解耦组件
// 创建服务上下文
const ApiServiceContext = React.createContext();
// 顶层注入服务实现
<ApiServiceContext.Provider value={new ApiService()}>
<UserProfile />
</ApiServiceContext.Provider>
// 子组件消费服务
const UserProfile = () => {
const api = useContext(ApiServiceContext);
const [user, setUser] = useState(null);
useEffect(() => {
api.getUser().then(setUser);
}, []);
return <div>{user?.name}</div>;
};
19、代理模式 (Proxy)
- 核心思想:创建代理组件控制原始组件访问
- 优势:控制组件访问,实现权限控制
// 权限代理组件
const withAuth = (WrappedComponent) => (props) => {
const { isAuthenticated } = useAuth();
return isAuthenticated
? <WrappedComponent {...props} />
: <div>请先登录</div>;
};
// 使用代理
const PrivateDashboard = withAuth(Dashboard);
20、外观模式 (Facade Pattern)
- 核心思想:提供简化的接口,隐藏内部复杂逻辑
- 优势:简化接口,提高代码可读性
import React, { useState, useMemo } from 'react';
// 库存子系统
const InventoryService = {
checkAvailability: (productId, quantity) => {
// 模拟库存检查
return new Promise((resolve) => {
setTimeout(() => {
const inStock = Math.random() > 0.3; // 70%概率有库存
resolve(inStock);
}, 300);
});
},
updateInventory: (productId, quantity) => {
// 模拟库存更新
console.log(`更新库存: 产品 ${productId}, 数量 -${quantity}`);
return Promise.resolve();
}
};
// 支付子系统
const PaymentService = {
authorizePayment: (amount, cardDetails) => {
// 模拟支付授权
return new Promise((resolve) => {
setTimeout(() => {
const success = Math.random() > 0.2; // 80%支付成功概率
resolve({ success, transactionId: success ? `tx-${Math.random().toString(36).substring(2, 10)}` : null });
}, 500);
});
}
};
// 物流子系统
const ShippingService = {
scheduleDelivery: (address, items) => {
// 模拟发货安排
return new Promise((resolve) => {
setTimeout(() => {
resolve({ trackingId: `trk-${Math.random().toString(36).substring(2, 14)}`, estimatedDelivery: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000) });
}, 400);
});
}
};
// 外观服务 - 提供统一的结账接口
class CheckoutFacade {
static async checkout(orderData) {
const { items, paymentMethod, shippingAddress } = orderData;
try {
// 1. 检查库存
const availabilityChecks = await Promise.all(
items.map(item => InventoryService.checkAvailability(item.productId, item.quantity))
);
if (availabilityChecks.some(available => !available)) {
throw new Error('某些商品库存不足');
}
// 2. 处理支付
const totalAmount = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const paymentResult = await PaymentService.authorizePayment(totalAmount, paymentMethod);
if (!paymentResult.success) {
throw new Error('支付授权失败');
}
// 3. 安排物流
const shippingResult = await ShippingService.scheduleDelivery(shippingAddress, items);
// 4. 更新库存
await Promise.all(
items.map(item => InventoryService.updateInventory(item.productId, item.quantity))
);
return {
success: true,
transactionId: paymentResult.transactionId,
trackingId: shippingResult.trackingId,
estimatedDelivery: shippingResult.estimatedDelivery.toISOString().split('T')[0]
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
}
const CheckoutPage = () => {
const [cartItems] = useState([
{ productId: 'p1', name: '商品 A', price: 29.99, quantity: 2 },
{ productId: 'p2', name: '商品 B', price: 49.99, quantity: 1 }
]);
const [shippingInfo, setShippingInfo] = useState({
name: '',
address: '',
city: ''
});
const [paymentInfo, setPaymentInfo] = useState({
cardNumber: '',
expiry: '',
cvv: ''
});
const [checkoutResult, setCheckoutResult] = useState(null);
const [loading, setLoading] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
const orderData = {
items: cartItems,
shippingAddress: shippingInfo,
paymentMethod: paymentInfo
};
const result = await CheckoutFacade.checkout(orderData);
setCheckoutResult(result);
setLoading(false);
};
const totalAmount = useMemo(() => {
return cartItems.reduce((sum, item) => sum + item.price * item.quantity, 0);
}, [cartItems]);
const updateShipping = (field, value) => {
setShippingInfo(prev => ({ ...prev, [field]: value }));
};
const updatePayment = (field, value) => {
setPaymentInfo(prev => ({ ...prev, [field]: value }));
};
if (loading) {
return (
<div className="checkout-loading">
<h2>处理结账中...</h2>
<div className="spinner"></div>
</div>
);
}
if (checkoutResult) {
return (
<div className="checkout-result">
{checkoutResult.success ? (
<>
<h2 className="success">订单创建成功!</h2>
<div className="order-details">
<p>交易号: {checkoutResult.transactionId}</p>
<p>物流跟踪号: {checkoutResult.trackingId}</p>
<p>预计送达时间: {checkoutResult.estimatedDelivery}</p>
</div>
<button onClick={() => setCheckoutResult(null)}>返回</button>
</>
) : (
<>
<h2 className="error">结账失败</h2>
<p>{checkoutResult.error}</p>
<button onClick={() => setCheckoutResult(null)}>重试</button>
</>
)}
</div>
);
}
return (
<div className="checkout-container">
<h2>结账</h2>
<form onSubmit={handleSubmit}>
<div className="checkout-section">
<h3>配送信息</h3>
<div className="form-group">
<label>姓名:</label>
<input
type="text"
value={shippingInfo.name}
onChange={(e) => updateShipping('name', e.target.value)}
required
/>
</div>
<div className="form-group">
<label>地址:</label>
<input
type="text"
value={shippingInfo.address}
onChange={(e) => updateShipping('address', e.target.value)}
required
/>
</div>
<div className="form-group">
<label>城市:</label>
<input
type="text"
value={shippingInfo.city}
onChange={(e) => updateShipping('city', e.target.value)}
required
/>
</div>
</div>
<div className="checkout-section">
<h3>支付信息</h3>
<div className="form-group">
<label>卡号:</label>
<input
type="text"
value={paymentInfo.cardNumber}
onChange={(e) => updatePayment('cardNumber', e.target.value)}
placeholder="1234 5678 9012 3456"
required
/>
</div>
<div className="form-group-row">
<div className="form-group">
<label>有效期:</label>
<input
type="text"
value={paymentInfo.expiry}
onChange={(e) => updatePayment('expiry', e.target.value)}
placeholder="MM/YY"
required
/>
</div>
<div className="form-group">
<label>CVV:</label>
<input
type="text"
value={paymentInfo.cvv}
onChange={(e) => updatePayment('cvv', e.target.value)}
placeholder="123"
required
/>
</div>
</div>
</div>
<div className="checkout-summary">
<h3>订单摘要</h3>
<ul className="cart-items">
{cartItems.map((item, index) => (
<li key={index}>
{item.name} × {item.quantity}: ${(item.price * item.quantity).toFixed(2)}
</li>
))}
</ul>
<div className="total-amount">
总计: <strong>${totalAmount.toFixed(2)}</strong>
</div>
</div>
<button type="submit" className="submit-btn">
确认支付
</button>
</form>
</div>
);
};
export default CheckoutPage;