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

Next.js中Socket.io客户端事件重复触发两次的问题求助

解决Next.js中Socket.io事件触发两次的问题

核心原因

  • 组件渲染时useEffect重复绑定socket.on监听器,未在组件卸载/重新渲染时清理旧监听器。
  • Next.js开发环境的Strict Mode会强制组件渲染两次,进一步导致监听器被重复注册。

解决方案

1. 给useEffect添加监听器清理逻辑

每次useEffect执行前,先移除之前绑定的同名监听器,避免重复注册:

useEffect(() => {
  // 单独定义监听回调函数,方便后续移除
  const handleNewMessage = (data: MessageData) => {
    setMessages((prev: MessageData[]) => [...prev, data])
  };

  // 绑定监听器
  socket.on("createMessage", handleNewMessage);

  // 组件卸载/effect重新触发时,移除对应的监听器
  return () => {
    socket.off("createMessage", handleNewMessage);
  };
}, [socket]);

2. 优化Socket实例的创建(可选)

当前代码在组件外部创建Socket实例,Next.js服务器端渲染时可能出现实例复用问题,建议将初始化逻辑移到客户端组件内部:
修改socket.context.tsx:

import { createContext, FC, useContext, useEffect, useState } from "react";
import { io, Socket } from "socket.io-client";
import { getCookie } from "cookies-next";

//types
interface SocketContextInterface {
  socket: Socket | null;
}
const SocketContext = createContext<SocketContextInterface>({ socket: null });

interface Props {
  children: React.ReactNode;
}

const SocketProvider: FC<Props> = (props) => {
  const [socket, setSocket] = useState<Socket | null>(null);

  useEffect(() => {
    // 仅在客户端初始化Socket连接
    const token = getCookie("session");
    const newSocket = io(process.env.NEXT_PUBLIC_WS || "http://localhost:5000", {
      extraHeaders: {
        authorization: token?.toString() || "",
      },
    });

    setSocket(newSocket);

    // 组件卸载时关闭Socket连接
    return () => {
      newSocket.close();
    };
  }, []);

  return (
    <SocketContext.Provider value={{ socket }}>
      {props.children}
    </SocketContext.Provider>
  );
};

export const useSocket = () => useContext(SocketContext);

export default SocketProvider;

使用时需先判断Socket实例是否存在:

const { socket } = useSocket();

useEffect(() => {
  if (!socket) return;

  const handleNewMessage = (data: MessageData) => {
    setMessages((prev) => [...prev, data]);
  };

  socket.on("createMessage", handleNewMessage);
  return () => socket.off("createMessage", handleNewMessage);
}, [socket]);

3. 关于Strict Mode的说明

Next.js开发环境的Strict Mode是框架的调试机制,会触发两次渲染,生产环境会自动禁用。只要监听器的清理逻辑正确,生产环境不会出现事件重复触发的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 06:30:47