试图弄清楚如何在使用 axios get 请求获取消息的 useEffect 中以正确的方式使用 socket.io

Trying to figure out how to use socket.io the correct way in a useEffect that is using an axios get request to fetch messages

到目前为止,我一直停留在我的 useEffect 上,它获取所有当前消息并相应地呈现状态。截至目前,在刷新页面之前它不会呈现新状态。

const Home = ({ user, logout }) => {
  const history = useHistory();

  const socket = useContext(SocketContext);

  const [conversations, setConversations] = useState([]);
  const [activeConversation, setActiveConversation] = useState(null);

  const classes = useStyles();
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  const addSearchedUsers = (users) => {
    const currentUsers = {};

    // make table of current users so we can lookup faster
    conversations.forEach((convo) => {
      currentUsers[convo.otherUser.id] = true;
    });

    const newState = [...conversations];
    users.forEach((user) => {
      // only create a fake convo if we don't already have a convo with this user
      if (!currentUsers[user.id]) {
        let fakeConvo = { otherUser: user, messages: [] };
        newState.push(fakeConvo);
      }
    });

    setConversations(newState);
  };

  const clearSearchedUsers = () => {
    setConversations((prev) => prev.filter((convo) => convo.id));
  };

  const saveMessage = async (body) => {
    const { data } = await axios.post("/api/messages", body);
    return data;
  };

  const sendMessage = (data, body) => {
    socket.emit("new-message", {
      message: data.message,
      recipientId: body.recipientId,
      sender: data.sender,
    });
  };

  const postMessage = async (body) => {
    try {
      const data = await saveMessage(body);

      if (!body.conversationId) {
        addNewConvo(body.recipientId, data.message);
      } else {
        addMessageToConversation(data);
      }
      sendMessage(data, body);
    } catch (error) {
      console.error(error);
    }
  };
  const addNewConvo = useCallback(
    (recipientId, message) => {
      conversations.forEach((convo) => {
        if (convo.otherUser.id === recipientId) {
          convo.messages.push(message);
          convo.latestMessageText = message.text;
          convo.id = message.conversationId;
        }
      });
      setConversations(conversations);
    },
    [setConversations, conversations],
  );

  const addMessageToConversation = useCallback(
    (data) => {
      // if sender isn't null, that means the message needs to be put in a brand new convo
      const { message, sender = null } = data;
      if (sender !== null) {
        const newConvo = {
          id: message.conversationId,
          otherUser: sender,
          messages: [message],
        };
        newConvo.latestMessageText = message.text;
        setConversations((prev) => [newConvo, ...prev]);
      }
      conversations.forEach((convo) => {
        console.log('hi', message.conversationId)
        if (convo.id === message.conversationId) {
          const convoCopy = { ...convo }
          convoCopy.messages.push(message);
          convoCopy.latestMessageText = message.text;
          console.log('convo', convoCopy)

        } else {
          return convo
        }
      });
      setConversations(conversations);
    },
    [setConversations, conversations],
  );

  const setActiveChat = useCallback((username) => {
    setActiveConversation(username);
  }, []);

  const addOnlineUser = useCallback((id) => {
    setConversations((prev) =>
      prev.map((convo) => {
        if (convo.otherUser.id === id) {
          const convoCopy = { ...convo };
          convoCopy.otherUser = { ...convoCopy.otherUser, online: true };
          return convoCopy;
        } else {
          return convo;
        }
      }),
    );
  }, []);

  const removeOfflineUser = useCallback((id) => {
    setConversations((prev) =>
      prev.map((convo) => {
        if (convo.otherUser.id === id) {
          const convoCopy = { ...convo };
          convoCopy.otherUser = { ...convoCopy.otherUser, online: false };
          return convoCopy;
        } else {
          return convo;
        }
      }),
    );
  }, []);

  // Lifecycle

  useEffect(() => {
    // Socket init
    socket.on("add-online-user", addOnlineUser);
    socket.on("remove-offline-user", removeOfflineUser);
    socket.on("new-message", addMessageToConversation);

    return () => {
      // before the component is destroyed
      // unbind all event handlers used in this component
      socket.off("add-online-user", addOnlineUser);
      socket.off("remove-offline-user", removeOfflineUser);
      socket.off("new-message", addMessageToConversation);
    };
  }, [addMessageToConversation, addOnlineUser, removeOfflineUser, socket]);

  useEffect(() => {
    // when fetching, prevent redirect
    if (user?.isFetching) return;

    if (user && user.id) {
      setIsLoggedIn(true);
    } else {
      // If we were previously logged in, redirect to login instead of register
      if (isLoggedIn) history.push("/login");
      else history.push("/register");
    }
  }, [user, history, isLoggedIn]);

  useEffect(() => {
    const fetchConversations = async () => {
      try {
        const { data } = await axios.get("/api/conversations");
        setConversations(data);
      } catch (error) {
        console.error(error);
      }
    };
    if (!user.isFetching) {
      fetchConversations();
    }
  }, [user]);

  const handleLogout = async () => {
    if (user && user.id) {
      await logout(user.id);
    }
  };

  return (
    <>
      <Button onClick={handleLogout}>Logout</Button>
      <Grid container component="main" className={classes.root}>
        <CssBaseline />
        <SidebarContainer
          conversations={conversations}
          user={user}
          clearSearchedUsers={clearSearchedUsers}
          addSearchedUsers={addSearchedUsers}
          setActiveChat={setActiveChat}
        />
        <ActiveChat
          activeConversation={activeConversation}
          conversations={conversations}
          user={user}
          postMessage={postMessage}
        />
      </Grid>
    </>
  );
};
这是我工作的主要部分,该项目在我开始时有起始代码,并被告知不要接触后端,所以我知道前端代码有问题。我觉得我错过了一些对 socket.io

很重要的东西

import { io } from 'socket.io-client';

import React from 'react';


export const socket = io(window.location.origin);
socket.on('connect', () => {
  console.log('connected to server');
});

export const SocketContext = React.createContext();

这就是我设置 socket.io 的方式,如果有人能指出正确的方向,那就太好了。我一直在尽可能多地阅读 socket.io,但仍然很迷茫。

基于后端正常工作的假设...

  const addNewConvo = useCallback(
    (recipientId, message) => {
      conversations.forEach((convo) => {
        if (convo.otherUser.id === recipientId) {
          convo.messages.push(message);
          convo.latestMessageText = message.text;
          convo.id = message.conversationId;
        }
      });
      setConversations(conversations);
    },
    [setConversations, conversations],
  );

setConversations(conversations);

这是使用状态变量设置状态的错误方法,因此它不会执行任何操作。可能是为什么您的代码在刷新之前不会更改。

建议的修复:

  const addNewConvo = useCallback(
    (recipientId, message) => {
        setConversations(previousState => previousState.map(convo => {
        if (convo.otherUser.id === recipientId) {
            convo.messages.push(message)
            convo.latestMessageText = message.text;
            convo.id = message.conversationId;
            return convo
        }
        return convo
      }))
    },
    [setConversations, conversations],
  );

注意:由于我对消息进行了深度复制,因此甚至可以更有效地完成上述操作