You need to enable JavaScript to run this app. React项目使用PubNub SDK开发聊天组件出现消息重复/无消息问题
优惠活动
大模型
产品
解决方案
定价
更多

React项目使用PubNub SDK开发聊天组件出现消息重复/无消息问题

问题根因
  • PubNub监听器无法正常移除:pubnub.removeListener要求传入和addListener完全一致的回调引用,当前代码在清理函数中重新定义了匿名回调,和注册时的回调不是同一个引用,导致旧监听器永远残留。每次切换频道都会新增一个监听器,同一条消息会被多次捕获执行消息插入逻辑,直接导致消息重复渲染。
  • 发送消息逻辑存在闭包陷阱:负责发送消息的useEffect依赖项配置错误,仅声明了sendMessage作为依赖,但sendMessage是每次组件渲染都会重新生成的新函数,会导致effect无意义重复触发;同时effect内部用到的message、chatRoom没有加入依赖项,会读取到旧的状态值,导致消息发送失败、不展示。
修复代码
import { useState, useEffect, useCallback } from 'react';

export default function Chat({ chatRoom, person, task }) {
  const [messages, setMessages] = useState([]);
  const [message, setMessage] = useState("");

  // 用useCallback包裹监听器回调,保证引用稳定
  const handlePubnubMessage = useCallback((message) => {
    if (message) {
      setMessages((prev) => [...prev, message]);
      console.log(message.message);
    }
    scrollingComponent.current?.scrollIntoView({ behavior: "smooth" });
  }, []);

  const handlePubnubFile = useCallback((file) => {
    // 把你原有handleFile的逻辑移到此处
  }, []);

  useEffect(() => {
    console.log("ADDING LISTENER");
    const listener = {
      message: handlePubnubMessage,
      file: handlePubnubFile
    };
    pubnub.addListener(listener);
    pubnub.subscribe({ channels: [chatRoom] });

    // 拉取当前频道历史消息
    pubnub.history({
      channel: chatRoom,
      count: 50 // 可根据需求调整拉取的历史消息数量
    }).then((res) => {
      setMessages(res.messages.map(item => item.entry));
    });

    // 清理函数直接传同一个listener对象即可正常移除监听器
    return () => {
      pubnub.removeListener(listener);
      pubnub.unsubscribe({ channels: [chatRoom] });
      setMessages([]);
    };
  }, [chatRoom, handlePubnubMessage, handlePubnubFile]);

  // 直接把发送逻辑写到sendMessage里,避免多余useEffect带来的问题
  const sendMessage = async () => {
    if (!message.trim()) return;
    try {
      await pubnub.publish({
        channel: chatRoom,
        message: message,
        storeInHistory: true,
      });
      await Promise.all([
        pubnub.signal({ channel: chatRoom, message: "notification" }),
        pubnub.signal({ channel: chatRoom, message: "not_typing" })
      ]);
      setMessage("");
    } catch (error) {
      setMessages((prev) => [...prev, error.toString()]);
    }
  };

  // 输入框提交事件直接绑定sendMessage即可,不需要额外监听message状态的useEffect
  // code continues...
}
额外注意点
  • 滚动操作前加可选链?.判断scrollingComponent是否存在,避免组件卸载时执行逻辑报错。
  • 取消订阅时指定对应频道,不要用unsubscribeAll,避免影响其他组件的PubNub订阅逻辑。

内容的提问来源于stack exchange,提问作者Vasil Kostadinov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.12 15:00:05