You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AI聊天机器人:新消息自动滚动与禁止空消息发送实现

解决AI聊天机器人的两个问题:自动滚动到底部和禁止空消息发送

一、实现收发消息时自动滚动到底部

之前添加h-full未生效,是因为缺少消息更新后的主动滚动触发逻辑。可以通过useRef获取消息容器节点,在消息列表更新时自动滚动到底部:

  1. 在App组件中创建DOM引用:
const messagesEndRef = useRef(null);
  1. 添加监听消息列表变化的副作用,每次更新后滚动到锚点位置:
useEffect(() => {
  messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
  1. 在消息列表末尾添加锚点元素:
<div className="w-full">
  {messages.map((message, index) => (
    <ChatMessage key={index} message={message.message} type={message.type} />
  ))}
  <div ref={messagesEndRef} />
</div>

这样无论是发送消息还是收到机器人回复,页面都会自动跳转到最新消息位置。

二、禁止发送空消息

修改sendMessage函数,先判断输入内容(去除前后空格后)是否为空,为空则终止发送逻辑:

const sendMessage = ($event) => {
  // 区分键盘Enter触发和按钮点击触发,非Enter键直接返回
  if($event.type === 'keydown' && $event.key !== "Enter") return;
  
  // 去除前后空格后判断是否为空
  const trimmedMessage = input_message.trim();
  if (!trimmedMessage) return;
  
  if (socket) {
    setMessages((prevMessages) => [
      ...prevMessages,
      {
        type: "send",
        message: trimmedMessage,
      },
    ]);
    socket.emit("message", trimmedMessage);
    setInputMessage("");
  }
};

修改后的完整代码

import { io } from "socket.io-client";
import { useState, useEffect, useRef } from "react";

function ChatMessage( {message, type}) {
  return(
    <div className={`flex w-full ${type == "send" ?  "justify-end" : "justify-start"}`}>
      {type == "send" ? (
        <div className="bg-green-300 p-2 m-3 rounded-b-xl rounded-tl-xl shadow-md" >
          {message}
          </div>
      ) : ( 
        <div className="bg-gray-300 p-2 m-3 rounded-b-xl rounded-tr-xl shadow-md" >
        {message}
        </div>
      )}
    </div>
  )
}

function App() {
  const [socket, setSocket] = useState(null);
  const [input_message, setInputMessage] = useState("");
  const [messages, setMessages] = useState([]);
  const messagesEndRef = useRef(null);

  useEffect(() => {
    const newSocket = io("http://localhost:8080");
    setSocket(newSocket);

    newSocket.on("response", (message) => {
      setMessages((prevMessages) => [
        ...prevMessages,
        {
          type: "recieve",
          message,
        },
      ]);
    });

    return () => newSocket.close();
  }, []);

  useEffect(() => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  }, [messages]);

  const sendMessage = ($event) => {
    if($event.type === 'keydown' && $event.key !== "Enter") return;
    
    const trimmedMessage = input_message.trim();
    if (!trimmedMessage) return;
    
    if (socket) {
      setMessages((prevMessages) => [
        ...prevMessages,
        {
          type: "send",
          message: trimmedMessage,
        },
      ]);
      socket.emit("message", trimmedMessage);
      setInputMessage("");
    }
  };

  return (
    <div className="p-5 h-screen bg-gradient-to-b from-green-950 to-green-200">
      <div className="container mx-auto bg-blend-screen opacity-90 h-full flex flex-col">
        <div className="flex-grow p-3 flex flex-col items-end overflow-auto">
          <div className="w-full">
            {messages.map((message, index) => (
              <ChatMessage key={index} message={message.message} type={message.type} />
            ))}
            <div ref={messagesEndRef} />
          </div>
        </div>
        <div className="h-[100px] p-3 flex justify-center items-center bg-green-700 opacity-65">
          <input
            onKeyDown={sendMessage}
            value={input_message}
            onChange={(e) => setInputMessage(e.target.value)}
            placeholder="Ask me anything...."
            type="text"
            className="w-full p-2 bg-transparent text-white border-white border-2 rounded-md outline-none"
          />
          <button onClick={sendMessage} className="bg-white px-3 py-2 rounded-md mx-2 cursor-pointer">
            Send
          </button>
        </div>
      </div>
    </div>
  );
}

export default App;

内容的提问来源于stack exchange,提问作者Da'ad Al-Faleh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 15:40:54