用React实现一个类 chatGPT 的交互式问答组件的方法详解

 更新时间:2023年06月29日 09:47:15   作者:hahayq  
这篇文章主要给大家详细介绍如何用React实现一个类 chatGPT 的交互式问答组件的方法,文中有详细的代码示例,对我们学习有一定的帮助,需要的朋友可以参考下

API文档

属性名描述类型默认值
recommendList推荐模板列表string[] | undefinednull
commonUseList常用模板列表string[] | undefinednull
uploadProps文件上传,同 antd Upload 组件{ accept?: string; children?: ReactNode; maxCount?: number; multiple?: boolean; } | undefinednull
autoCompleteProps输入框自动补全,同 antd AutoComplete 组件{ options?: DefaultOptionType[]; onSearch?: ((value: string) => void); } | undefinednull
onSend发送消息((msg: IQuestion) => void) | undefinednull
onMsgLike点赞回答((msg: IQuestion) => void) | undefinednull
onMsgNotLike点踩回答((msg: IQuestion) => void) | undefinednull
onRefresh刷新回答((msg: IQuestion) => void) | undefinednull
renderAvatar自定义头像((msg: IMessage) => ReactNode) | undefinednull
renderTools自定义消息框底部操作栏((msg: IMessage) => ReactNode) | undefinednull
isAnimation是否开启消息动画效果boolean | undefinednull
className自定义类名boolean | undefinednull
style自定义样式CSSProperties | undefinednull
loading加载状态boolean | undefinednull
messages消息列表IMessage[] | undefinednull
# 功能介绍
这个组件的主要功能主要包括:
  • 输入框上传附件
  • 输入框自动补全
  • 自定义消息操作栏
  • 自定义头像
  • 模板调用

下面会挨个介绍功能

输入框上传文件

这个功能很简单就是支持输入框上传附件,然后会把附件和问题一起丢给使用者,由使用者自己去调用api接口来获取回答.上传组件采用的是antd的Upload组件,API也一致.大致代码如下:

import React from 'react';
import { Chat, useMessage, EMessageType } from './chat';
export default function () {
  const { msgProps } = useMessage(
    (question, lastAnswer) => {
      return new Promise((resolve, reject) => {
        const { content, fileList } = question;
        setTimeout(() => {
          resolve({
            content:
              '\n\n' +
              (lastAnswer ? '## 我又想了一下 \n\n' : '') +
              content?.replace(/吗/g, '呢').replace(/你/g, '我') +
              '\n\n' +
              fileList.map((item) => item.name).join(' ') +
              '\n\n![lena](https://img1.baidu.com/it/u=2041864569,220923158&fm=253&fmt=auto&app=138&f=JPEG?w=300&h=284)'
          });
        }, 2000);
      });
    },
    {
      uploadProps: {
        multiple: true
      },
      defaultMsgs: [
        {
          key: 1,
          type: EMessageType.AI,
          content: '快来和我对话吧',
          renderTools: () => null,
          isAnimation: false
        }
      ]
    }
  );
  return (
      <Chat {...msgProps} />
  );
}

输入框自动补全

自动补全采用的是antd的Autocomplete组件,API也一致,原理就是当输入框内容改变时去请求关联内容然后改变Autocomplete的options,就能实现自动补全啦.

import React, { useState } from 'react';
import { Chat, useMessage, EMessageType, IMessage } from './chat';
export default function () {
  const [options, setOptions] = useState<{ value: string }[]>([]);
  const handleSearch = (value: string) => {
    setOptions(!value ? [] : [{ value }, { value: value + value }, { value: value + value + value }]);
  };
  const { msgProps } = useMessage(
    (question, lastAnswer) => {
      return new Promise((resolve, reject) => {
        const { content } = question;
        setTimeout(() => {
          resolve({
            isAnimation: !!lastAnswer,
            content:
              '\n\n' +
              (lastAnswer ? '## 我又想了一下 \n\n' : '') +
              content?.replace(/吗/g, '呢').replace(/你/g, '我') +
              '\n\n![lena](https://img1.baidu.com/it/u=2041864569,220923158&fm=253&fmt=auto&app=138&f=JPEG?w=300&h=284)'
          });
        }, 2000);
      });
    },
    {
      autoCompleteProps: {
        options,
        onSearch: handleSearch
      }
    }
  );
  return (
      <Chat {...msgProps} />
  );
}

自定义消息操作栏

由于每条消息下方还有操作栏,默认的有重新回答、点赞、点踩、复制等,所以提供了自定义的功能,如果不需要默认的这些功能也可以关闭或者自定义.

import React from 'react';
import { Chat, useMessage, EMessageType, IMessage } from './chat';
export default function () {
  const { msgProps } = useMessage(
    (question, lastAnswer) => {
      return new Promise((resolve, reject) => {
        const { content } = question;
        setTimeout(() => {
          resolve({
            content:
              '\n\n' +
              (lastAnswer ? '## 我又想了一下 \n\n' : '') +
              content?.replace(/吗/g, '呢').replace(/你/g, '我') +
              '\n\n![lena](https://img1.baidu.com/it/u=2041864569,220923158&fm=253&fmt=auto&app=138&f=JPEG?w=300&h=284)',
            renderTools: (msg: IMessage) => <div>点个赞吧</div>
          });
        }, 2000);
      });
    },
    {
      renderTools: (msg: IMessage) => <div>这里是自定义操作栏</div>,
      defaultMsgs: [
        {
          key: 1,
          type: EMessageType.AI,
          content: '快来和我对话吧',
          renderTools: () => null,
          isAnimation: false
        },
        {
          key: 2,
          type: EMessageType.AI,
          content: '支持每条消息独立设置操作栏',
          renderTools: (msg: IMessage) => <div>这里是消息独立操作栏</div>,
          isAnimation: false
        }
      ]
    }
  );
  return (
    <BixiProvider locale={enUS}>
      <Chat {...msgProps} />
    </BixiProvider>
  );
}

自定义头像

既然是对话,那肯定得提供自定义头像的功能啦,使用方式的话是传入renderAvatar这个方法,通过传入的消息类型来判断显示那个头像,效果如下

import React from 'react';
import { Chat, useMessage, EMessageType, IMessage } from './chat';
export default function () {
  const { msgProps } = useMessage(
    (question, lastAnswer) => {
      return new Promise((resolve, reject) => {
        const { content } = question;
        setTimeout(() => {
          resolve({
            content:
              '\n\n' +
              (lastAnswer ? '## 我又想了一下 \n\n' : '') +
              content?.replace(/吗/g, '呢').replace(/你/g, '我')
          });
        }, 2000);
      });
    },
    {
      renderAvatar: (msg: IMessage) => <>{msg.type === EMessageType.AI ? '人工智障' : '大帅比'}</>,
      defaultMsgs: [
        {
          key: 1,
          type: EMessageType.AI,
          content: '快来和我对话吧',
          renderTools: () => null,
          isAnimation: false
        },
        {
          key: 2,
          type: EMessageType.Self,
          content: '你真的是人工智能吗',
          isAnimation: false
        },
        {
          key: 3,
          type: EMessageType.AI,
          content: '支持每条消息单独设置头像',
          isAnimation: false
        }
      ]
    }
  );
  return (
      <Chat {...msgProps} />
  );
}

模板调用

这个功能是比赛要求的,需要给用户提供一些模板,用户点击模板就可以快速搜索答案之类的,在输入框输入“/”就可以唤醒模板了,实现方式也很简单,监听输入框值的改变,如果输入值和“/”匹配就直接唤起模板就可以了.效果如下

import React from 'react';
import { Chat, useMessage, EMessageType, IMessage } from './chat';
export default function () {
  const { msgProps } = useMessage(
    (question, lastAnswer) => {
      return new Promise((resolve, reject) => {
        const { content } = question;
        setTimeout(() => {
          resolve({
            content:
              '\n\n' +
              (lastAnswer ? '## 我又想了一下 \n\n' : '') +
              content?.replace(/吗/g, '呢').replace(/你/g, '我')
          });
        }, 2000);
      });
    },
    {
      recommendList: ['标题生成', '文章续写', '文章润色', '文章大纲', '朋友圈文案', '活动方案', '翻译', '演讲稿'],
      commonUseList: ['标题生成', '朋友圈文案'],
    }
  );
  return (
      <Chat {...msgProps} />
  );
}

自定义hook

除了上面的功能外,还对逻辑进行了封装,调用这个hook可以获取到messages、onRefresh、onSend、loading这几个主要的props,其他的props你也可以自己传入. 这个自定义hook封装了消息发送方法,消息刷新方法,组件loading状态,消息列表等主要逻辑.重新回答的原理是去消息列表中寻找之前最近的一条问题,然后在此调用获取回答的接口 代码如下:

import { useState, useCallback } from 'react';
import { IMessage, IChatProps, EMessageType, IQuestion } from './model';
import { uniqueId } from 'lodash-es';
type IUseMessageOption = Omit<IChatProps, 'onSend' | 'onRefresh' | 'loading' | 'messages'> & {
  defaultMsgs: IMessage[];
};
type IService = (msg: IQuestion, lastAnswer?: IMessage) => Promise<IMessage>;
export function useMessage(
  getMsg: IService,
  { defaultMsgs, ...props }: IUseMessageOption
): {
  msgProps: IChatProps;
} {
  const [messageList, setMessageList] = useState<IMessage[]>(defaultMsgs || []);
  const [loading, setLoading] = useState(false);
  const onRefresh = useCallback(
    (item: IMessage) => {
      const index = messageList.findIndex((msg) => msg.key === item.key);
      if (index < 1) return;
      const lastQuestion = messageList
        .slice(0, index)
        .reverse()
        .find((msg) => msg.type === EMessageType.Self);
      if (!lastQuestion) return;
      setLoading(true);
      getMsg(lastQuestion, messageList[index]).then((res) => {
        setLoading(false);
        const key = `${EMessageType.AI}-${uniqueId()}`;
        messageList[index] = { ...res, key, type: EMessageType.AI };
        setMessageList([...messageList]);
      });
    },
    [getMsg, messageList]
  );
  const onSend = useCallback(
    (msg: IQuestion) => {
      setLoading(true);
      setMessageList((list) => list.concat({ ...msg, key: `${EMessageType.Self}-${uniqueId()}`, type: EMessageType.Self }));
      getMsg(msg).then((res) => {
        setLoading(false);
        const key = `${EMessageType.AI}-${uniqueId()}`;
        setMessageList((list) => list.concat({ ...res, key, type: EMessageType.AI }));
      });
    },
    [getMsg]
  );
  return {
    msgProps: {
      messages: messageList,
      onRefresh,
      onSend,
      loading,
      ...props
    }
  };
}

使用方法

该组件使用起来很简单,调用useMessage这个hook,传入api接口(需要返回promise)就可以了,至于其他的组件api也可以一并传入,还支持传入历史消息列表.然后这个hook就会返回所有该组件所需要的props,包括消息发送方法,消息刷新方法,组件loading状态,消息列表等.是不是很方便呢,哈哈.

以上就是用React实现一个类 chatGPT 的交互式问答组件的方法详解的详细内容,更多关于React chatGPT问答组件的资料请关注脚本之家其它相关文章!

相关文章

  • React hooks useState异步问题及解决

    React hooks useState异步问题及解决

    这篇文章主要介绍了React hooks useState异步问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • 浅谈对于react-thunk中间件的简单理解

    浅谈对于react-thunk中间件的简单理解

    这篇文章主要介绍了浅谈对于react-thunk中间件的简单理解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • React星星评分组件的实现

    React星星评分组件的实现

    评分插件在购物的应用中经常可以看得到,但是用着别人的总是没有自己写的顺手,本文就使用React实现星星评分组件,感兴趣的可以了解一下
    2021-06-06
  • React列表栏及购物车组件使用详解

    React列表栏及购物车组件使用详解

    这篇文章主要为大家详细介绍了React列表栏及购物车组件使用,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • 使用react-beautiful-dnd实现列表间拖拽踩坑

    使用react-beautiful-dnd实现列表间拖拽踩坑

    相比于react-dnd,react-beautiful-dnd更适用于列表之间拖拽的场景,本文主要介绍了使用react-beautiful-dnd实现列表间拖拽踩坑,感兴趣的可以了解一下
    2021-05-05
  • react路由守卫的实现(路由拦截)

    react路由守卫的实现(路由拦截)

    react不同于vue,通过在路由里设置meta元字符实现路由拦截。本文就详细的介绍一下,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • React不能将useMemo设置为默认方法原因详解

    React不能将useMemo设置为默认方法原因详解

    这篇文章主要为大家介绍了React不能将useMemo设置为默认方法原因详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪<BR>
    2022-07-07
  • react app rewrited替代品craco使用示例

    react app rewrited替代品craco使用示例

    这篇文章主要为大家介绍了react app rewrited替代品craco使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • React中redux的使用详解

    React中redux的使用详解

    Redux 是一个状态管理库,它可以帮助你管理应用程序中的所有状态,Redux的核心概念之一是Store,它表示整个应用程序的状态,这篇文章给大家介绍React中redux的使用,感兴趣的朋友一起看看吧
    2023-12-12
  • react lazyLoad加载使用详解

    react lazyLoad加载使用详解

    lazy是React提供的懒(动态)加载组件的方法,React.lazy(),路由组件代码会被分开打包,能减少打包体积、延迟加载首屏不需要渲染的组件,依赖内置组件Suspense标签的fallback属性,给lazy加上loading指示器组件,Suspense目前只和lazy配合实现组件等待加载指示器的功能
    2023-03-03

最新评论