React Hooks + Firebase Firestore onSnapshot - 正确使用带有反应钩子的 Firestore 侦听器 [英] React Hooks + Firebase Firestore onSnapshot - Correct use of a firestore listener with react hooks

查看:30
本文介绍了React Hooks + Firebase Firestore onSnapshot - 正确使用带有反应钩子的 Firestore 侦听器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设您有一个带有数据库侦听器的屏幕,该侦听器是在 useEffect 中创建的,此侦听器的目的是增加屏幕中的计数器,就像这样:

Imagine that you have a screen with a database listener which is created in a useEffect, and the purpose of this listener is to increment a counter in your screen, just something like this:

function MyScreen(props) {
     const [magazinesCounter, setMagazinesCounter] = useState(0);

     const handleMagazinesChanges = (snapshot) => {
         const changes = snapshot.docChanges();

         let _magazinesCounter = magazinesCounter;
       
         changes.forEach((change) => {
            if (change.type === "added") {
               _magazinesCounter  += 1;
            }
            if (change.type === "removed") {
               _magazinesCounter  -= 1;
            }
         });
         
         setMagazinesCounter(_magazinesCounter);  
     };

     useEffect(() => {
         const query = props.db.collection("content")
                        .where("type", "==", "magazine");
         
         // Create the DB listener
         const unsuscribe = query.onSnapshot(handleMagazinesChanges, (err) => {});
         return () => {
            unsuscribe();
         }
     }, []);
}

正如您在此处看到的,这将不起作用,因为在状态更新时不会重新创建 useEffect 侦听器中使用的 handleMagazinesChanges...

As you can see here, this will not work because the handleMagazinesChanges which is used in the useEffect's listener is not re-created when the state updates...

因此,我尝试修复将 magazinesCounter 作为依赖项传递给 useEffect 的问题,如下所示:

So, I tried to fix that passing the magazinesCounter as dependency to the useEffect, like this:

 useEffect(() => {
     // ... the same stuff
 }, [magazinesCounter]);

但是这样,我们将进入无限循环,因为将重新创建侦听器,并且

But with this, we will enter into an endless loop because the listener will be re-created, and

 if (change.type === "added") {
     _magazinesCounter  += 1;
 }
 ...
 setMagazinesCounter(_magazinesCounter);

将被再次执行,一次又一次...

will be executed again, and again...

Pd:另外,将 handleMagazinesChanges 函数包装在 useCallback 中,并以 MagazinesCounter 作为依赖项,然后将该函数作为依赖项传递给 useEffect,将具有相同的效果...

Pd: Also, wrapping the handleMagazinesChanges function in a useCallback with the magazinesCounter as dependency, and then passing the function as dependency to the useEffect, will have the same effect...

那么,我该如何解决这种情况?我知道,如果我们用 useRef 对相同数据使用辅助引用,我们可以成功执行此操作,避免无限循环.但是,有没有其他更好的方法来做到这一点?我的意思是,没有状态 + 对最新数据的引用:

So, how can I fix this situation? I know that if we use an auxiliar reference to the same data with useRef, we can perform this operation succesfully, avoiding the endless loop. But, is there any other better way to do that? I mean, without having the state + a reference to the most freshed data:

// This works good, but is there any other better solution to this problem?
function MyScreen(props) {
     const [magazinesCounter, setMagazinesCounter] = useState(0);

     const magazinesCounterRef = useRef(magazinesCounter);

     const handleMagazinesChanges = (snapshot) => {
         const changes = snapshot.docChanges();

         changes.forEach((change) => {
            if (change.type === "added") {
               magazinesCounterRef.current  += 1;
            }
            if (change.type === "removed") {
               magazinesCounterRef.current  -= 1;
            }
         });

         setMagazinesCounter(magazinesCounterRef.current);  
     };

     useEffect(() => {
         const query = props.db.collection("content")
                        .where("type", "==", "magazine");

         // Create the DB listener
         const unsuscribe = query.onSnapshot(handleMagazinesChanges, (err) => {});
         return () => {
            unsuscribe();
         }
     }, []);
}

有什么想法吗?谢谢.

Pd:也许这是要走的路,但我想有更好的方法而不创建辅助变量.

Pd: Maybe this is the way to go, but I suppose there is a better way without creating auxiliar variables.

推荐答案

为了实现这一点,我有两个建议:

There are two recommendations I would have for achieving this:

  1. 将回调移动到 useEffect 内部以避免每次渲染时重新创建函数
  2. 在设置状态时使用回调来获取当前值(参见 React此处的文档) 以避免在状态更改时需要重新创建回调
  1. Move the callback inside of the useEffect to avoid re-creating the function each render
  2. Use a callback when setting state to get the current value (see the React Docs here) to avoid needing to re-create the callback when the state changes

在您当前的代码中使用这些:

Using these with your current code:

function MyScreen(props) {
     const [magazinesCounter, setMagazinesCounter] = useState(0);

     useEffect(() => {
         // Moved inside "useEffect" to avoid re-creating on render
         const handleMagazinesChanges = (snapshot) => {
             const changes = snapshot.docChanges();

             // Accumulate differences
             let difference  = 0;
             changes.forEach((change) => {
                if (change.type === "added") {
                   difference  += 1;
                }
                if (change.type === "removed") {
                   difference  -= 1;
                }
             });
             
             // Use the setState callback 
             setMagazinesCounter((currentMagazinesCounter) => currentMagazinesCounter + difference);  
         };

         const query = props.db.collection("content")
                        .where("type", "==", "magazine");
         
         // Create the DB listener
         const unsuscribe = query.onSnapshot(handleMagazinesChanges, 
  err => console.log(err));
         return () => {
            unsuscribe();
         }
     }, []);
}

这篇关于React Hooks + Firebase Firestore onSnapshot - 正确使用带有反应钩子的 Firestore 侦听器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆