UNPKG

react-use-chat

Version:

一个用于构建对话式引导流程的 React Hook,支持单选、多选和嵌套分支处理

465 lines (382 loc) 11 kB
# React Use Chat [![npm version](https://badge.fury.io/js/react-use-chat.svg)](https://badge.fury.io/js/react-use-chat) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/) 一个用于构建对话式引导流程的 React Hook,支持单选、多选和嵌套分支处理。 [English](./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(可选) 对于复杂的应用程序,你也可以使用 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> ); } ``` ## 📚 API 参考 ### useDialog Hook ```tsx const result = useDialog(dialogData, options); ``` #### 参数 | 参数 | 类型 | 必需 | 描述 | | --- | --- | --- | --- | | `dialogData` | `DialogNode[]` | ✅ | 对话节点数据数组 | | `options` | `UseDialogOptions` | ❌ | 配置选项 | #### 选项 (UseDialogOptions) ```tsx interface UseDialogOptions { initialNodeId?: string; // 初始节点 ID findNodeById?: (nodeId: string) => DialogNode | null; // 自定义节点查找函数 autoCompleteDelay?: number; // 自动完成延迟时间(毫秒) } ``` #### 返回值 (UseDialogReturn) ```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; // 重置对话 } ``` ### 数据结构 #### DialogNode ```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; // 是否为分支结束节点 } ``` #### DialogAnswer ```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; // 是否互斥 } ``` #### AnswerType ```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 #e1e1e1; background: white; cursor: pointer; transition: all 0.2s ease; } .answer-button:hover { border-color: #007bff; background: #f8f9fa; } ``` ## 🔧 开发 ### 安装依赖 ```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) - 轻量级状态管理 --- 如果这个项目对你有帮助,请给个 ⭐️ 支持一下!