react-use-chat
Version:
一个用于构建对话式引导流程的 React Hook,支持单选、多选和嵌套分支处理
465 lines (382 loc) • 11 kB
Markdown
[](https://badge.fury.io/js/react-use-chat)
[](https://opensource.org/licenses/MIT)
[](http://www.typescriptlang.org/)
一个用于构建对话式引导流程的 React Hook,支持单选、多选和嵌套分支处理。
[](./README.en.md) | 中文
- 🎯 **对话式交互** - 以聊天形式引导用户完成复杂流程
- 🎛️ **多种选择类型** - 支持单选、多选和自动完成
- 🌳 **嵌套分支处理** - 支持多层嵌套的复杂对话树
- 🎨 **灵活配置** - 支持自定义执行顺序和互斥选项
- 📝 **历史记录** - 自动记录用户的选择历史
- 🎁 **计划推荐** - 基于用户选择生成个性化推荐
- 📱 **响应式** - 支持桌面和移动端
- 🔧 **TypeScript** - 完整的类型定义支持
- 🪝 **React Hooks** - 现代 React 开发模式
- ⚡ **轻量级** - 无额外依赖,体积小巧
```bash
npm install react-use-chat
```
```bash
yarn add react-use-chat
```
```bash
pnpm add react-use-chat
```
```tsx
import React from 'react';
import { useDialog } from 'react-use-chat';
const dialogData = [
{
node_id: 'welcome',
question_text: '你想学习什么?',
answer_type: 'single_select',
answers: [
{
answer_id: 'math',
answer_text: '数学',
next_node_id: 'math_level',
plan_trigger: null,
},
{
answer_id: 'english',
answer_text: '英语',
next_node_id: null,
plan_trigger: '英语学习计划',
},
],
},
{
node_id: 'math_level',
question_text: '选择你的数学水平:',
answer_type: 'multi_select',
answers: [
{
answer_id: 'basic',
answer_text: '基础',
next_node_id: null,
plan_trigger: '基础数学',
},
{
answer_id: 'advanced',
answer_text: '高级',
next_node_id: null,
plan_trigger: '高级数学',
},
],
},
];
function App() {
const {
currentNode,
history,
selectedAnswers,
handleSelection,
handleMultiSelect,
confirmSelections,
resetDialog,
} = useDialog(dialogData);
if (!currentNode) {
return <div>对话已完成</div>;
}
return (
<div>
<h2>{currentNode.question_text}</h2>
{currentNode.answer_type === 'single_select' && (
<div>
{currentNode.answers.map((answer) => (
<button
key={answer.answer_id}
onClick={() => handleSelection(answer.answer_id)}
>
{answer.answer_text}
</button>
))}
</div>
)}
{currentNode.answer_type === 'multi_select' && (
<div>
{currentNode.answers.map((answer) => (
<label key={answer.answer_id}>
<input
type="checkbox"
checked={selectedAnswers.includes(answer.answer_id)}
onChange={() => handleMultiSelect(answer.answer_id)}
/>
{answer.answer_text}
</label>
))}
<button
onClick={confirmSelections}
disabled={selectedAnswers.length === 0}
>
确认选择
</button>
</div>
)}
</div>
);
}
```
对于复杂的应用程序,你也可以使用 Context Provider 方式:
```tsx
import React from 'react';
import { DialogProvider, useDialogContext } from 'react-use-chat';
function DialogComponent() {
const { currentNode, handleSelection } = useDialogContext();
// ... 组件逻辑
}
function App() {
return (
<DialogProvider dialogData={dialogData}>
<DialogComponent />
</DialogProvider>
);
}
```
```tsx
const result = useDialog(dialogData, options);
```
| 参数 | 类型 | 必需 | 描述 |
| --- | --- | --- | --- |
| `dialogData` | `DialogNode[]` | ✅ | 对话节点数据数组 |
| `options` | `UseDialogOptions` | ❌ | 配置选项 |
```tsx
interface UseDialogOptions {
initialNodeId?: string; // 初始节点 ID
findNodeById?: (nodeId: string) => DialogNode | null; // 自定义节点查找函数
autoCompleteDelay?: number; // 自动完成延迟时间(毫秒)
}
```
```tsx
interface UseDialogReturn {
currentNode: DialogNode | null; // 当前节点
history: HistoryItem[]; // 历史记录
learningPlan: string[]; // 学习计划
selectedAnswers: string[]; // 已选择的答案
multiSelectBranchStack: SelectionBranch[][]; // 多选分支栈
currentBranchIndices: number[]; // 分支索引
pendingPlanTriggers: string[]; // 待处理的计划触发器
handleSelection: (answerId: string) => void; // 处理单选
handleMultiSelect: (answerId: string) => void; // 处理多选
confirmSelections: () => void; // 确认多选
resetDialog: () => void; // 重置对话
}
```
```tsx
interface DialogNode {
node_id: string; // 节点唯一标识
question_text: string; // 问题文本
answer_type: AnswerType; // 答案类型
answers: DialogAnswer[]; // 答案选项
default_next_node_id?: string | null; // 默认下一节点(auto_complete 类型)
default_plan_trigger?: string | null; // 默认计划触发器
execute_by_config_order?: boolean; // 是否按配置顺序执行
is_branch_end?: boolean; // 是否为分支结束节点
}
```
```tsx
interface DialogAnswer {
answer_id: string; // 答案唯一标识
answer_text: string; // 答案文本
next_node_id: string | null; // 下一个节点 ID
plan_trigger: string | null; // 计划触发器
execution_order?: number; // 执行顺序
mutually_exclusive?: boolean; // 是否互斥
}
```
```tsx
type AnswerType = "single_select" | "multi_select" | "auto_complete";
```
系统支持复杂的嵌套多选场景:
```tsx
const complexDialogData = [
{
node_id: 'subjects',
question_text: '选择你想学习的学科:',
answer_type: 'multi_select',
execute_by_config_order: true,
answers: [
{
answer_id: 'math',
answer_text: '数学',
next_node_id: 'math_topics', // 指向另一个多选节点
plan_trigger: null,
execution_order: 0,
},
{
answer_id: 'english',
answer_text: '英语',
next_node_id: 'english_skills', // 指向另一个多选节点
plan_trigger: null,
execution_order: 1,
},
],
},
// 数学主题的多选节点
{
node_id: 'math_topics',
question_text: '选择数学学习重点:',
answer_type: 'multi_select',
answers: [
{
answer_id: 'algebra',
answer_text: '代数',
next_node_id: null,
plan_trigger: '代数学习计划',
},
{
answer_id: 'geometry',
answer_text: '几何',
next_node_id: null,
plan_trigger: '几何学习计划',
},
],
},
// ... 更多节点
];
```
在多选模式下,可以设置互斥选项:
```tsx
{
node_id: 'level_selection',
question_text: '选择你的水平和学习偏好:',
answer_type: 'multi_select',
answers: [
{
answer_id: 'beginner',
answer_text: '初学者',
mutually_exclusive: true, // 与其他水平选项互斥
next_node_id: null,
plan_trigger: '初学者计划',
},
{
answer_id: 'advanced',
answer_text: '高级',
mutually_exclusive: true, // 与其他水平选项互斥
next_node_id: null,
plan_trigger: '高级计划',
},
{
answer_id: 'visual_learner',
answer_text: '视觉学习者',
next_node_id: null,
plan_trigger: '视觉学习计划',
},
],
}
```
```tsx
{
node_id: 'processing',
question_text: '正在生成你的个性化学习计划...',
answer_type: 'auto_complete',
answers: [],
default_next_node_id: 'results',
default_plan_trigger: null,
}
```
```tsx
const { currentNode } = useDialog(dialogData, {
findNodeById: (nodeId) => {
// 自定义查找逻辑,比如从 API 获取
return fetch(`/api/nodes/${nodeId}`).then(res => res.json());
},
});
```
Hook 会在对话更新时派发滚动事件:
```tsx
import { SCROLL_TO_BOTTOM_EVENT } from 'react-use-chat';
useEffect(() => {
const handleScroll = () => {
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
};
document.addEventListener(SCROLL_TO_BOTTOM_EVENT, handleScroll);
return () => document.removeEventListener(SCROLL_TO_BOTTOM_EVENT, handleScroll);
}, []);
```
Hook 本身不包含样式,你可以完全自定义 UI。参考示例项目中的 CSS:
```css
.dialog-container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.question {
margin-bottom: 20px;
font-size: 18px;
font-weight: 600;
}
.answer-button {
display: block;
width: 100%;
margin-bottom: 10px;
padding: 12px 16px;
border: 2px solid
background: white;
cursor: pointer;
transition: all 0.2s ease;
}
.answer-button:hover {
border-color:
background:
}
```
```bash
npm install
```
```bash
npm run build
```
```bash
npm test
```
```bash
cd example
npm install
npm start
```
MIT © [Your Name](https://github.com/defaultjacky)
欢迎贡献代码!请阅读 [贡献指南](./CONTRIBUTING.md) 了解详情。
<a href="https://github.com/defaultjacky/react-use-chat/graphs/contributors">
<img src="https://contrib.rocks/image?repo=defaultjacky/react-use-chat" />
</a>
- 💬 [GitHub Discussions](https://github.com/defaultjacky/react-use-chat/discussions)
- 🐛 [GitHub Issues](https://github.com/defaultjacky/react-use-chat/issues)
- 📧 [邮件支持](mailto:your.email@example.com)
- [react-hook-form](https://github.com/react-hook-form/react-hook-form) - 性能优异的表单库
- [react-query](https://github.com/TanStack/query) - 数据获取和状态管理
- [zustand](https://github.com/pmndrs/zustand) - 轻量级状态管理
---
如果这个项目对你有帮助,请给个 ⭐️ 支持一下!