我有一个全局上下文,用于监听
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
状态才能更新它。
如果你的方法很适合你,那么它就没有问题。
但是,拥有代表单一事实来源的全局状态将使您避免潜在的错误和问题。
最好创造几样东西:
import { createContext } from 'react';
interface MessagesContextType { messages: Message[]; }
export const MessagesContext = createContext<MessagesContextType>({ messages: [] });
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>;
};
export const useMessagesContext = () => {
const context = useContext(MessagesContext);
if (!context) throw new Error('No MessagesContext provider is found');
return context;
};
<MessagesContextProvider socket={socket}>
<YourApp/>
<MessagesContextProvider/>
const messages = useMessages();
另外,请注意,在您的代码片段中,您为
[socket]
定义了 deps useEffect
,但您忘记将 pathname
添加到 deps。这意味着,如果 pathname
发生变化,事件监听器中的 pathname
不会改变!这是因为 javascript 中所谓的“闭包”(然而,闭包只是对 ecmascript 功能的一个天真的解释。您可以在 https://tc39.es/ecma262/ 中阅读它)