useChat Hook
概述
useChat
是一个功能完整的 React Hook,用于构建支持流式响应的聊天应用。它提供了消息管理、流式数据处理、错误处理、重试机制、会话管理等核心功能。
核心架构
1. 类型系统
1.1 基础消息结构
typescript
interface Message<TMetadata = Record<string, unknown>> {
id: string; // 唯一标识符
role: "user" | "assistant"; // 消息角色
content: string; // 消息内容
metadata?: TMetadata; // 扩展元数据
}
1.2 配置选项
typescript
interface ChatOptions<TMetadata> {
sendFullHistory?: boolean; // 是否发送完整历史
maxHistoryLength?: number; // 最大历史长度
headers?: Record<string, string>; // 自定义请求头
timeoutMs?: number; // 超时时间
maxRetries?: number; // 最大重试次数
retryDelayMs?: number; // 重试延迟
parseChunk?: (data: string) => any; // 数据解析函数
onStatusChange?: (status: ChatStatus) => void; // 状态变更回调
onChunkError?: (error: Error) => void; // 块错误处理
sessionId?: string; // 会话ID
metadata?: TMetadata; // 全局元数据
}
1.3 状态管理
typescript
type ChatStatus = "ready" | "submitted" | "streaming" | "error";
interface ChatError {
message: string;
code: "TIMEOUT" | "ABORT" | "NETWORK" | "HTTP" | "PARSE" | "UNKNOWN";
details?: unknown;
}
核心实现原理
2.1 流式数据处理架构
2.1.1 TransformStream 管道设计
useChat
采用现代 Web Streams API 构建了一个高效的数据处理管道:
scss
ReadableStream (HTTP响应)
↓
TextDecoderStream (二进制→文本)
↓
LineSplitterStream (按行分割)
↓
SSEParserStream (SSE解析)
↓
React State (消息更新)
2.1.2 LineSplitterStream 实现
typescript
function createLineSplitterStream(): TransformStream<string, string> {
let buffer = "";
return new TransformStream<string, string>({
transform(chunk, controller) {
buffer += chunk;
const lines = buffer.split(/\r?\n/);
buffer = lines.pop() || "";
for (const line of lines) {
if (line) controller.enqueue(line);
}
},
flush(controller) {
if (buffer) controller.enqueue(buffer);
},
});
}
工作原理:
- 维护一个缓冲区累积接收到的文本块
- 使用正则表达式
\r?\n
分割行 - 保留不完整的最后一行到缓冲区
- 流结束时推送剩余数据
2.1.3 SSEParserStream 实现
typescript
function createSSEParserStream<TMetadata>(
parseChunk: (data: string) => any,
onChunkError?: (error: Error) => void
): TransformStream<string, string> {
return new TransformStream<string, string>({
async transform(line, controller) {
const trimmedLine = line.trim();
if (trimmedLine.startsWith("data:")) {
const data = trimmedLine.slice(5).trim();
if (data && data !== "[DONE]") {
try {
const content = parseChunk(data);
if (content !== null) controller.enqueue(content);
} catch (err) {
onChunkError?.(err as Error);
}
}
}
},
});
}
SSE格式解析:
- 识别
data:
前缀的行 - 过滤空数据和
[DONE]
标记 - 支持自定义解析函数
- 错误隔离处理
2.2 状态管理机制
2.2.1 核心状态
typescript
const [messages, setMessages] = useState<Message<TMetadata>[]>([]);
const [status, setStatus] = useState<ChatStatus>("ready");
const [error, setError] = useState<ChatError | null>(null);
const [currentSessionId, setCurrentSessionId] = useState<string | undefined>();
2.2.2 引用管理
typescript
const abortControllerRef = useRef<AbortController | null>(null);
const lastRequestRef = useRef<{
message: string;
options: Partial<ChatOptions<TMetadata>>;
} | null>(null);
2.3 消息发送流程
2.3.1 完整生命周期
- 验证阶段:检查状态和消息有效性
- 状态更新:设置 "submitted" 状态
- 消息构建:创建用户消息和空助手消息
- 配置合并:合并全局配置和局部配置
- 历史处理:根据配置决定发送的历史消息
- 重试循环:支持指数退避重试
- 流式处理:通过管道处理响应数据
- 状态同步:实时更新消息内容和状态
2.3.2 流式数据处理流程
typescript
// 1. 创建桥接流
const readableStream = new ReadableStream<Uint8Array>({
start(controller) {
const pump = async () => {
try {
const { done, value } = await reader.read();
if (done) controller.close();
else {
controller.enqueue(value);
pump();
}
} catch (err) {
controller.error(err);
}
};
pump();
},
});
// 2. 构建处理管道
const pipeline = readableStream
.pipeThrough(new TextDecoderStream())
.pipeThrough(createLineSplitterStream())
.pipeThrough(createSSEParserStream(...));
// 3. 消费处理结果
const processedReader = pipeline.getReader();
while (true) {
const { done, value } = await processedReader.read();
if (done) break;
if (value) {
setMessages(prev => prev.map(msg =>
msg.id === assistantMessageId
? { ...msg, content: msg.content + value }
: msg
));
}
}
2.4 错误处理机制
2.4.1 错误分类
- TIMEOUT: 请求超时
- ABORT: 用户主动取消
- NETWORK: 网络错误
- HTTP: HTTP状态错误
- PARSE: 数据解析错误
- UNKNOWN: 未知错误
2.4.2 重试策略
typescript
let attempt = 0;
while (attempt < mergedOptions.maxRetries) {
try {
// 执行请求
break; // 成功则退出循环
} catch (err) {
if (isAbortOrTimeout(err)) {
// 不可重试错误,直接返回
return;
}
attempt++;
if (attempt < maxRetries) {
await delay(retryDelayMs);
}
}
}
2.5 会话管理
2.5.1 会话ID处理
- 支持通过
options.sessionId
初始化会话 - 动态切换会话时自动清空消息
- 支持通过
setMessages
更新会话ID
2.5.2 会话持久化
typescript
useEffect(() => {
if (options.sessionId !== currentSessionId) {
clearMessages();
setCurrentSessionId(options.sessionId);
}
}, [options.sessionId]);
高级特性
3.1 元数据支持
通过 TypeScript 泛型支持任意类型的元数据:
typescript
interface MyMetadata {
confidence?: number;
sources?: string[];
timestamp?: Date;
}
const { sendMessage } = useChat<MyMetadata>('/api/chat', {
metadata: { confidence: 0.95 }
});
3.2 自定义数据解析
支持自定义 parseChunk
函数处理不同格式的响应:
typescript
const parseChunk = (data: string) => {
const parsed = JSON.parse(data);
if (parsed.type === 'delta') {
return parsed.content;
}
return null;
};
3.3 生命周期钩子
onStatusChange
: 状态变更通知onChunkError
: 块级错误处理
使用示例
4.1 基础用法
typescript
import useChat from '@/hooks/use-chat';
function ChatComponent() {
const { messages, status, error, sendMessage } = useChat('/api/chat');
const handleSend = async (message: string) => {
await sendMessage(message);
};
return (
<div>
{messages.map(msg => (
<div key={msg.id} className={msg.role}>
{msg.content}
</div>
))}
</div>
);
}
4.2 高级配置
typescript
const { messages, status, sendMessage, abortRequest } = useChat('/api/chat', {
sendFullHistory: true,
maxHistoryLength: 50,
timeoutMs: 10000,
maxRetries: 3,
retryDelayMs: 1000,
headers: { 'Authorization': 'Bearer token' },
onStatusChange: (status) => console.log('Status:', status),
parseChunk: (data) => {
try {
const parsed = JSON.parse(data);
return parsed.choices[0]?.delta?.content || null;
} catch {
return null;
}
}
});
性能优化
5.1 内存管理
- 自动清理超时的 AbortController
- 组件卸载时自动中止请求
- 使用 useCallback 优化重渲染
5.2 流式处理优势
- 内存占用恒定,不受响应大小影响
- 实时更新,无需等待完整响应
- 支持任意大小的响应数据
错误调试
6.1 调试技巧
typescript
const { error, status } = useChat('/api/chat', {
onStatusChange: console.log,
onChunkError: console.error,
});
// 错误处理
if (error) {
console.error('Chat error:', error.code, error.message, error.details);
}
6.2 常见问题
- 网络超时 :调整
timeoutMs
配置 - CORS错误:检查服务器配置
- 解析错误 :验证
parseChunk
函数实现 - 状态异常 :检查
status
状态机
扩展指南
7.1 集成其他UI库
支持无缝集成各种UI组件库,只需处理返回的 messages
和 status
。
7.2 自定义存储
通过 setMessages
和 getMessages
实现自定义存储:
typescript
// 保存到localStorage
useEffect(() => {
localStorage.setItem('chat', JSON.stringify(messages));
}, [messages]);
// 从localStorage恢复
const loadMessages = () => {
const saved = localStorage.getItem('chat');
if (saved) setMessages(JSON.parse(saved));
};
API 参考
useChat 参数
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
apiUrl | string | - | 聊天API端点 |
options | ChatOptions | {} | 配置选项 |
返回值
属性名 | 类型 | 描述 |
---|---|---|
messages | Message[] | 当前消息列表 |
status | ChatStatus | 当前状态 |
error | ChatError | null | 错误信息 |
sendMessage | function | 发送消息函数 |
abortRequest | function | 中止当前请求 |
clearMessages | function | 清空消息列表 |
retry | function | 重试上次请求 |
setMessages | function | 设置消息列表 |
resetConversation | function | 重置会话 |