useWebSocket.ts 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. "use client";
  2. import { useCallback, useEffect, useRef } from "react";
  3. import { WSClient, WSMessage } from "@/lib/websocket";
  4. interface UseWebSocketOptions<T> {
  5. conversationId: number | null;
  6. enabled?: boolean;
  7. isVisitor?: boolean; // 是否是访客(默认为 true)
  8. agentId?: number; // 客服ID(如果是客服连接,需要传递)
  9. wsToken?: string; // 客服 WS 令牌(登录后下发)
  10. onMessage: (payload: WSMessage<T>) => void;
  11. onError?: (error: Event) => void;
  12. onClose?: () => void;
  13. }
  14. export function useWebSocket<T>({
  15. conversationId,
  16. enabled = true,
  17. isVisitor = true, // 默认是访客
  18. agentId,
  19. wsToken,
  20. onMessage,
  21. onError,
  22. onClose,
  23. }: UseWebSocketOptions<T>) {
  24. // 使用 useRef 存储最新的回调函数,避免因回调函数变化导致重新连接
  25. const onMessageRef = useRef(onMessage);
  26. const onErrorRef = useRef(onError);
  27. const onCloseRef = useRef(onClose);
  28. const clientRef = useRef<WSClient<T> | null>(null);
  29. // 更新 ref 的值
  30. useEffect(() => {
  31. onMessageRef.current = onMessage;
  32. onErrorRef.current = onError;
  33. onCloseRef.current = onClose;
  34. }, [onMessage, onError, onClose]);
  35. useEffect(() => {
  36. if (!conversationId || !enabled) {
  37. clientRef.current?.disconnect();
  38. clientRef.current = null;
  39. return;
  40. }
  41. // 客服端必须带 wsToken;否则后端会 401,且所有实时能力(新消息/草稿/已读)都不可用。
  42. if (!isVisitor && !wsToken) {
  43. clientRef.current?.disconnect();
  44. clientRef.current = null;
  45. return;
  46. }
  47. const client = new WSClient<T>({
  48. conversationId,
  49. isVisitor,
  50. agentId,
  51. wsToken,
  52. // 使用 ref 的 current 值,这样即使回调函数变化也不会导致重新连接
  53. onMessage: (payload) => onMessageRef.current(payload),
  54. onError: onErrorRef.current
  55. ? (error) => onErrorRef.current?.(error)
  56. : undefined,
  57. onClose: onCloseRef.current ? () => onCloseRef.current?.() : undefined,
  58. });
  59. clientRef.current = client;
  60. client.connect();
  61. return () => {
  62. client.disconnect();
  63. if (clientRef.current === client) {
  64. clientRef.current = null;
  65. }
  66. };
  67. // 只依赖 conversationId、enabled、isVisitor 和 agentId,不依赖回调函数
  68. // 回调函数通过 useRef 存储,不会导致重新连接
  69. }, [conversationId, enabled, isVisitor, agentId, wsToken]);
  70. const send = useCallback((type: string, data?: unknown): boolean => {
  71. return clientRef.current?.send(type, data) ?? false;
  72. }, []);
  73. return { send };
  74. }