首页 > 解决方案 > 如何使用useEffect正确处理firebase实时数据库监听器?

问题描述

const Messages = (props) => {
      const [messages, setMessages] = useState([]);
      const { currentChannel, currentUser } = props;
      const messagesRef = firebase.database().ref("messages");
    
      useEffect(() => {
        if (currentChannel && currentUser) {
          const channel = currentChannel.currentChannel;
          let loadedMessages = [];
          messagesRef.child(channel.id).on("child_added", (snap) => {
            loadedMessages.push(snap.val());
          });
          setMessages(loadedMessages);
        }
    
        return () => messagesRef.off();
      }, [currentChannel, currentUser, messagesRef]);
    
      const displayMessages = (messages) =>
        messages.length > 0 &&
        messages.map((message) => (
          <Message key={message.timestamp} message={message} user={currentUser} />
        ));
    
      return (
        <>
          <MessagesHeader />
          <Segment>
            <Comment.Group className="messages">
              {displayMessages(messages)}
            </Comment.Group>
          </Segment>
          <MessageForm
            messagesRef={messagesRef}
            currentChannel={currentChannel}
            currentUser={currentUser}
          />
        </>
      );
    };

警告:已超出最大更新深度。当组件调用setStateinside时,可能会发生这种情况useEffect,但useEffect要么没有依赖数组,要么每次渲染时其中一个依赖项都发生了变化。

如何防止这种无限循环?

标签: node.jsreactjsfirebase-realtime-database

解决方案


每次渲染时,您都在创建一个新messagesRef对象。它可能正在处理数据库中的同一点,但它是一个新对象。更改messageRef后,您的效果将重新运行,并且setMessages每次都会使用全新的数组调用该效果。由于您设置了状态,因此组件会重新呈现,并且循环会重复。

大多数情况下,我建议让数据库 ref 只存在于您useEffect的 . 但是,看起来您需要将 ref 作为道具传递给MessageForm,所以这在这里不起作用。相反,您需要确保只创建一次数据库引用。useMemo是一种方法:

const messagesRef = useMemo(() => {
  return firebase.database().ref("messages");
}, []);

推荐阅读