遵循 Linus Torvalds 的工程化理念:
- 简单优于复杂:避免过度抽象
- 明确的职责边界:状态管理、业务逻辑、副作用分离
- 类型安全:统一的类型定义
- 错误处理:要么正确处理,要么让它崩溃
- 性能优先:避免不必要的重渲染
┌─────────────────────────────────────┐
│ Components (UI) │
│ - ChatView, ChatHeader, etc. │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Context (State Management) │
│ - ChatContext (shared state) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Hooks (React Integration) │
│ - useMessageSender │
│ - useFileHandler │
│ - useDraftPersistence │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Pure Functions (Business Logic) │
│ - chat-logic.ts │
│ - file-utils.ts │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ API Layer (Data Access) │
│ - actions.ts │
└─────────────────────────────────────┘
统一的类型定义,避免重复:
ChatType,MentionDraft,ImageAttachmentChatState,ChatMetadataMessageActionParams,MessageActionResult
纯函数,易于测试:
isPauseRequest()- 判断是否为暂停请求filterPendingRequests()- 过滤待处理请求calculateMessageTargets()- 计算消息目标
使用 Context 避免 prop drilling:
- 集中管理 input, images, draftMentions
- 提供 clearInput, clearAll 等便捷方法
const { send } = useMessageSender({
type: "agent",
pendingRequests,
onSuccess: refreshLatest,
});- 使用对象参数,易于扩展
- 从 Context 获取状态,减少参数传递
- 依赖数组优化,避免不必要的重新创建
const { handleFileInput, handlePaste } = useFileHandler({
inputWrapRef,
});- 统一的文件处理逻辑
- 使用 Promise.allSettled 处理批量上传
- 完善的错误处理和日志记录
useDraftPersistence({ type, id });- 自动加载和保存草稿
- 防抖优化,减少 localStorage 写入
- 错误处理不影响主流程
统一的错误处理机制:
ChatError- 自定义错误类型handleError()- 统一错误消息格式化logError()- 错误日志记录withErrorHandling()- 高阶函数包装
function ChatView({ type, id, name }: ChatViewProps) {
return (
<ChatProvider>
<ChatViewContent type={type} id={id} name={name} />
</ChatProvider>
);
}
function ChatViewContent({ type, id }: Props) {
const { input, busy } = useChatContext();
useDraftPersistence({ type, id });
const { send } = useMessageSender({
type,
pendingRequests,
onSuccess: refreshLatest,
});
const { handleFileInput, handlePaste } = useFileHandler({
inputWrapRef,
});
return (
<div>
<textarea
value={input}
onPaste={handlePaste}
/>
<button onClick={send} disabled={busy}>
Send
</button>
</div>
);
}- 减少依赖数组:使用 Context 减少 hook 参数
- 防抖保存:草稿保存延迟 300ms
- useRef 缓存:避免闭包陷阱
- Promise.allSettled:并行处理文件上传
- 明确的 memo 边界:只在必要时使用 useMemo
- 纯函数测试:
chat-logic.ts中的函数易于单元测试 - Hook 测试:使用
@testing-library/react-hooks - 集成测试:测试 Context + Hooks 的组合
- 错误场景:测试错误处理和边界情况
从旧架构迁移到新架构:
- 用
ChatProvider包裹组件 - 替换
useState为useChatContext() - 使用新的 hooks 替代旧的实现
- 移除重复的类型定义
- 更新错误处理逻辑