Когда я получаю новое сообщение от Firebase onSnapshot, мой контейнер входящих сообщений не перерисовывается? (огневая база, реагируй, зустанд)

# #reactjs #firebase #google-cloud-firestore #react-hooks #zustand

Вопрос:

всякий раз, когда я получаю новое сообщение из своей коллекции сообщений через onSnapshot, мои входящие сообщения не отправляются повторно.

Я могу подтвердить, что новое сообщение определенно хранится в a useRef , как я вижу его в инструментах разработки, однако сообщение не попадает в DOM.

Я следовал zustand's документации, в которой объясняется, что рекомендуется использовать ссылку и их методы подписки для повторных изменений состояния: https://github.com/pmndrs/zustand#transient-updates-for-often-occuring-state-changes

Кто-нибудь может понять, в чем я, возможно, ошибаюсь?

Страница сообщений, на которой отображаются разговоры и обновления в реальном времени:

 import { useState, useEffect, useRef } from "react";
import "./Messages.scss";
import { db } from "../../firebase";
import { Message, Conversation } from "../../Interfaces/Interfaces";
import MessagesHeader from "./Components/MessagesHeader/MessagesHeader";
import MessagesConversations from "./Components/MessagesConversations/MessagesConversations";
import create from "zustand";
import authStore from "../../Global/AuthState/AuthStore";

interface ConversationsStore {
  conversations: Array<Conversation>;
  setConversations: (newConversations: Array<Conversation>) => void;
}

const conversationsStore = create<ConversationsStore>((set) => ({
  conversations: [],
  setConversations: (newConversations: Array<Conversation>) =>
    set({ conversations: newConversations }),
}));

function Messages() {
  const currentUser = authStore((state) => state.currentUser);
  const [subToUpdates, setSubToUpdates] = useState<boolean>(false);
  const [initialDone, setInitialDone] = useState<boolean>(false);
  //create the initial conversations ref
  const conversationsRef = useRef<Array<Conversation>>(
    conversationsStore.getState().conversations
  );
  const conversations = conversationsStore((state) => state.conversations);
  const setConversations = conversationsStore(
    (state) => state.setConversations
  );

  //zustand's recommended subscribe useEffect to re-render whenever a new conversation enters the db
  useEffect(
    () =>
      conversationsStore.subscribe(
        (state) => (conversationsRef.current = state.conversations)
      ),
    []
  );

  //get the current conversations that the user is in
  useEffect(() => {
    if (currentUser) {
      let initialConversations: Array<Conversation> = [];
      const conversationsRef = db.collection("conversations");
      const getInitialConversations = async () => {
        const conversationsSnapshot = await conversationsRef
          .where("users", "array-contains", currentUser.uid)
          .orderBy("latestMessageDate", "desc")
          .get();
        conversationsSnapshot.forEach((doc) => {
          let tempConversation: Conversation = doc.data() as Conversation;
          tempConversation.conversationId = doc.id;
          initialConversations.push(tempConversation);
          setConversations(initialConversations);
        });
      };
      getInitialConversations().then(() => setSubToUpdates(true));
    }
  }, [currentUser]);

  //after the initial load, listen to changes in the firestore collection
  useEffect(() => {
    if (subToUpdates amp;amp; currentUser amp;amp; !initialDone) {
      setInitialDone(true);
    } else if (subToUpdates amp;amp; currentUser amp;amp; initialDone) {
      const conversationsRef = db.collection("conversations");
      return conversationsRef
        .where("users", "array-contains", currentUser.uid)
        .orderBy("latestMessageDate", "desc")
        .limit(1)
        .onSnapshot((querySnapshot) => {
          console.log("new conversation found");
          querySnapshot.forEach((doc) => {
            {
              /* 
            let tempConversations = conversations?.filter(
              (currentConversation) =>
                currentConversation.conversationId == doc.id
            );
            */
            }
            let tempConversations = conversations;
            let tempConversation: Conversation = doc.data() as Conversation;
            tempConversation.conversationId = doc.id;
            tempConversations?.push(tempConversation);
            setConversations(tempConversations);
          });
        });
    }
  }, [subToUpdates, initialDone]);

  return (
    <div className="messages">
      {conversations.length > 0 amp;amp;
        conversations.map((conversation, index) => (
          <h4 key={index}>{conversation.latestMessage.message}</h4>
        ))}
      <MessagesHeader />
      <MessagesConversations
        conversations={conversationsRef.current}
        filterByUnread={false}
      />
    </div>
  );
}

export default Messages;
 

Контейнер «Входящие разговоры», который отображает разговоры и показывает их:

 import React from "react";
import "./MessagesConversations.scss";
import MessagesConversationsConversation from "./Components/MessagesConversationsConversation/MessagesConversationsConversation";
import { Conversation } from "../../../../Interfaces/Interfaces";

interface Props {
  conversations: Array<Conversation> | undefined;
  filterByUnread: boolean;
}

function MessagesConversations({ conversations, filterByUnread }: Props) {
  return (
    <div className="messagesConversations">
      {conversations !== undefined amp;amp;
        (conversations?.length > 0 ? (
          conversations.map((conversation) => (
            <MessagesConversationsConversation
              conversation={conversation}
              key={`conversation__${conversation.conversationId}`}
            />
          ))
        ) : (
          <h4>No messages</h4>
        ))}
    </div>
  );
}

export default MessagesConversations;