【问题标题】:UseEffect infinite loop with Firebase使用 Firebase 的 UseEffect 无限循环
【发布时间】:2021-03-14 21:09:06
【问题描述】:

您好,我正在尝试构建一个简单的聊天,但在阅读消息时遇到了一些问题。

  const [messages, setMessages] = useState([]);
  const messagesArray = [];
  const messagesRef = firestore().collection('messages');

useEffect(() => {
    id
      ? messagesRef
          .where('idRequest', '==', id)
          .orderBy('createdAt')
          .limit(25)
          .get()
          .then((response) => {
            response.forEach((doc) => {
              messagesArray.push(doc.data());
            });
            setMessages(messagesArray);
            console.log('Changes: ', messages);
          })
      : console.log('No ID provided.');
  }, [messages]); 

如果我使用依赖项 'messages' 运行它,它可以工作,但应用程序变得非常慢,所以我放置了一个 console.log 来检查 useEffect 的流量,我认为它在无限循环,一遍又一遍地寻找变化。我假设发生这种情况是因为每当 useEffect 运行时,它都会“设置”messages 数组,从而使其再次运行。 我可以通过从依赖数组中删除“messages”来解决循环问题,但问题是在发送或接收新消息时它不会再次呈现。所以我不确定我应该怎么做。

PS:聊天取决于实际的用户 ID,开始的条件是确保收到 ID,否则不应查询 firestore db。

【问题讨论】:

  • 嗨。在推送消息并调用 setMessages 之前,我们能否不检查检索到的消息是否已经在 messagesArray 中?我们无法从依赖项中删除消息,但我们可以检查检索到的消息是否已经存在
  • 我不确定如何实现这一点,我对编程有点陌生。你的意思是,以某种方式获取 messagesArray 的值,并将其与状态中的新消息进行比较?我不知道我怎么能这样做

标签: reactjs firebase react-native google-cloud-firestore react-hooks


【解决方案1】:

您在UseEffect 内调用SetMessages(),这会更改messages,从而触发您的useEffect。

你的 linter 应该已经抓住了这个。

【讨论】:

  • linter 建议我在依赖项中添加:id、messages 和 messagesArray。我试过了,但它的行为方式相同。
【解决方案2】:

既然您说您需要将消息数组作为 useEffect 的依赖项,您可以做的是在 ref 中保留消息数组的副本,并将其与您的 useEffect 回调中的消息变量进行比较。

const [messages, setMessages] = useState([]);
const messagesArray = [];
const messagesRef = firestore().collection("messages");

const lastMessages = useRef();

useEffect(() => {
  // Only do something if the messages variable was reassigned outside of the useEffect
  if (messages !== lastMessages.current) {
    id
      ? messagesRef
          .where("idRequest", "==", id)
          .orderBy("createdAt")
          .limit(25)
          .get()
          .then((response) => {
            response.forEach((doc) => {
              messagesArray.push(doc.data());
            });

            // Update the value of the ref
            lastMessages.current = messagesArray;

            setMessages(messagesArray);

            console.log("Changes: ", messages);
          })
      : console.log("No ID provided.");
  }
}, [messages]);

这是可行的,因为即使组件重新渲染,也会保留 ref 的值。

【讨论】:

    【解决方案3】:

    检查下面的代码。

      const [messages, setMessages] = useState([]);
      const messagesArray = [];
      const messagesRef = firestore().collection('messages');
    
    useEffect(() => {
        id
          ? messagesRef
              .where('idRequest', '==', id)
              .orderBy('createdAt')
              .limit(25)
              .get()
              .then((response) => {
                response.forEach((doc) => {
                  //check here if the message is already in the messages array 
                  //before pushing it and calling setMessages. Use findIndex or 
                  //filter method to do so.
    
                    //something like this
                   const messageExists = messages.findIndex(doc.data());
                   if (!messageExists){
                  messagesArray.push(doc.data());
                  setMessages(messagesArray);
                });
              })
          : console.log('No ID provided.');
      }, [messages]); 
    

    【讨论】:

      【解决方案4】:

      您在useEffect 中使用messages 的唯一时间是console.log('Changes: ', messages) - 到那时甚至都无效。

      删除该 console.log 语句,messages 将不再是依赖项。没有更多的循环。 idmessageArray 是实际的依赖,应该添加;在useEffect中没有一个修改,所以没有循环。

      如果您需要console.log('Changes: ', messages),请不要在 useEffect 中使用 - 创建另一个 JUST FOR THE CONSOLE.LOG

      useEffect(() => {
        console.log('Changes: ', messages);
      }, [messages]);
      

      这只会在消息改变时被调用,并且不会修改消息,所以没有循环。

      【讨论】:

        【解决方案5】:

        我遇到了这个问题,以下似乎有效。就像@Thor0o0 说的那样,您需要一个条件语句来检查您是否已经在您的状态下捕获了消息。

        我会使用id 作为依赖项,而不是messages

        const [messages, setMessages] = useState([]);
        const messagesRef = firestore().collection('messages');
        
        useEffect(() => {
          if(!id) return console.log('No ID provided.');
          const messagesArray = messages;
        
          messagesRef
            .where('idRequest', '==', id)
            .orderBy('createdAt')
            .limit(25)
            .get()
            .then((response) => {
              response.forEach((doc) => {
               const foundMsgIndex = messagesArray.findIndex(msg=>msg.id===doc.id)
               if(foundMsgIndex < 0){ 
                   messagesArray.push({...doc.data(), id:doc.id});
               }
              });
              setMessages(messagesArray);
              console.log('Changes: ', messages);
              }) 
          }, [id]); 
        

        【讨论】:

          猜你喜欢
          • 2020-09-29
          • 2020-07-01
          • 2020-08-19
          • 2020-01-31
          • 2020-02-22
          • 2021-01-22
          • 2021-01-31
          • 2020-04-14
          相关资源
          最近更新 更多