在套接字io/react中为同一事件添加多个事件监听器

问题描述 投票:0回答:1

我有一个全局上下文,用于监听

new-message
事件。

SocketContext.js(全局)

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

         socket.on("new_message", (data) => {
            // Dont run the code if we're on /chat (as that means there is another listener with same name attatched)
            if (pathname === "/chat") return              

            // Update chats latest message. This should only run if we aren't on /chat route
            dispatch(updateChat({ data: data.message}))
        })

        return () => socket.off("new_message")
}, [socket])

我有一个聊天组件,当收到新消息时,应更新本地

messages
状态

Chat.js(当用户访问/聊天路径时呈现)

const [messages, setMessages) = useState([])

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

  socket.on("new_message", (data) => {
      setMessages((prev) => [...prev, data.message])
  })

  return () => socket.off("new_message")
}, [socket])

两个组件监听同一个事件。但两者都会根据用户是否位于

/chat
路线上执行不同的操作。如果路由是
/chat
,但事件仍然被触发并被全局监听(即使我们在 /chat 路由上监听相同的事件),全局事件监听器内的代码将不会运行。

这是否被认为是不好的做法(在不同的地方添加两个同名的事件监听器)?一种替代方法是全局添加一个事件侦听器,但是这样我就必须全局存储

messages
状态才能更新它。

javascript reactjs socket.io
1个回答
0
投票

如果你的方法很适合你,那么它就没有问题。

但是,拥有代表单一事实来源的全局状态将使您避免潜在的错误和问题。

最好创造几样东西:

  1. 创建上下文:
import { createContext } from 'react';

interface MessagesContextType { messages: Message[]; }

export const MessagesContext = createContext<MessagesContextType>({ messages: [] });
  1. 创建上下文提供者:
import React, { PropsWithChildren, useEffect } from 'react';

interface Props {
   socket: Socket;
}

export const MessagesContextProvider: React.FC<PropsWithChildren<Props>> = ({ socket, children }) => {
    const [messages, setMessages] = useState([]);

    useEffect(() => {
        if (!socket) return;
        socket.on('new_message', (data) => {
            setMessages((prev) => [...prev, data.message]);
        });
        return () => socket.off('new_message');
    }, [socket]);
    
    const value: MessagesContextType = { messages}

    return <MessagesContext.Provider value={value}>{children}</MessagesContext.Provider>;
};

  1. 创建一个钩子:
export const useMessagesContext = () => {
    const context = useContext(MessagesContext);
    if (!context) throw new Error('No MessagesContext provider is found');
    return context;
};
  1. 使用 MessagesContextProvider 将 jsx 树全局包裹起来:
<MessagesContextProvider socket={socket}>
  <YourApp/>
<MessagesContextProvider/>
  1. 在需要读取消息的地方使用钩子(例如聊天组件):
const messages = useMessages();

另外,请注意,在您的代码片段中,您为

[socket]
定义了 deps
useEffect
,但您忘记将
pathname
添加到 deps。这意味着,如果
pathname
发生变化,事件监听器中的
pathname
不会改变!这是因为 javascript 中所谓的“闭包”(然而,闭包只是对 ecmascript 功能的一个天真的解释。您可以在 https://tc39.es/ecma262/

中阅读它)
© www.soinside.com 2019 - 2024. All rights reserved.