React Hooks + Firebase Firestore onSnapshot-正确使用带有React Hooks的Firestore侦听器

维克多·莫利纳(Victor Molina)

问题

假设您有一个屏幕,其中包含一个在useEffect中创建的数据库侦听器,此侦听器的目的是在屏幕上增加一个计数器,如下所示:

(仅使用不带依赖项的useEffect挂钩)

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。

因此,我尝试解决了将MagazinesCounter作为对useEffect的依赖项传递的问题,如下所示:

(使用useEffect挂钩,并将修改后的状态作为依赖项)

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

但是有了这个,我们将进入一个无穷循环,因为将重新创建侦听器,并且

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

将被再次执行...

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

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

(没有依赖项的useEffect + useRef)

// 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();
         }
     }, []);
}

有任何想法吗?谢谢。

帕德:也许这是要走的路,但是我想有一个更好的方法而不创建辅助变量。

土豆泥

为此,我有两个建议:

  1. 将回调移动到的内部,useEffect以避免在每次渲染时重新创建函数
  2. 设置状态以获取当前值时使用回调(请参见此处React Docs),以避免在状态更改时需要重新创建回调

在当前代码中使用它们:

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) => {});
         return () => {
            unsuscribe();
         }
     }, []);
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何从Firebase Firestore快照(React Hooks)获取对象?

Firebase / Cloud Firestore:onSnapshot()与on()

从node.js调用firestore方法时,如何使用firestore的onSnapshot侦听器?

在React Redux上使用FireStore onSnapShot的正确方法

如何从 Firestore Firebase 中的 onSnapshot 侦听器跳过初始数据?

删除 Firebase Firestore 查询 onSnapshot

如何使用 firestore 和 express.js 创建 onSnapshot 侦听器

Firestore - 集合的 React JS 实时侦听器

如何限制Firestore的onSnapshot侦听器返回的数据?

在 Firestore onSnapshot 侦听器中访问反应状态的问题

如何使用onSnapshot回调获取一个异步的React函数调用firebase.firestore.set等待?

React Firebase UseEffect Firestore

为什么更新状态未反映在事件侦听器中:React Native,Hooks

Firebase Firestore onSnapshot 在使用 Firestore FieldValue 作为时间戳时为 null

使用 React Hooks 限制 Firebase 重新渲染

Firestore是否具有与Firebase的childAdded等效的侦听器(使用swift)?

带有React Hooks的HoC

带有React && Hooks的IntersectionObserver

如果用户关闭浏览器,Firestore是否会继续阅读onSnapshot侦听器的文档?

使用附加的侦听器对Firebase RTDB或Firestore进行本地写入是否需要读取操作?

NextJS:react-firebase-hooks命令

Firebase和React Hooks(useState和useEffect)

在react-native上使用onSnapshot()时,显示Firestore时间戳会出错

多人游戏的内存泄漏/无限循环问题:React State和Firestore快照侦听器

如何取消订阅 React-Native 中的 Firestore 侦听器?

在响应本机中将firestore嵌套在onSnapshot侦听器中?

如果我在 Cloud Function 中对 Firestore 实施 onSnapshot 实时侦听器,会花费更多吗?

带有React Quill和Firebase Firestore的评论回复系统

Firebase Firestore onSnapshot-使用Promise在初始数据查询后获取实时更新