在React Native中的useEffect异步函数中返回一个清理函数

3
我正在使用Firebase的onSnapshot方法来在React Native应用程序中使用实时数据,并编写一个单独的异步函数来监听Firestore,如下所示:
async function getData() {

    let collName = await AsyncStorage.getItem('@collection_name')
    let subscriber = shopReference.collection(collName).onSnapshot((snap) => {
        let menuList = [];
        snap.forEach((doc) => {
          menuList.push({
            id: doc.id,
            ...doc.data(),
          });
        });
    
        setMenu(menuList);
      });

      return subscriber;
}

我正在使用useEffect调用此函数,并希望将从firestore返回的订阅器方法作为清理函数返回。我知道可以通过在useEffect函数中直接实现此代码来实现这一点。但由于我正在使用AsyncStorage来检索集合ID,因此需要将其放在异步功能中。由于getData()方法返回promise而不是subscriber function,我无法返回其响应。最好的解决方案是什么?
编辑:
我尝试了以下所有方法来调用useEffect函数内部的函数。
// Without returning anything, but according to the firestore doc. This may cause data leaks.
useEffect(() => {
        getData();
      }, []);

// directly returning the method, this cause error that promise cant be accepted as a clean up function.
useEffect(() => {
        return getData();
      }, []);



useEffect(() => {
        getData().then((subscriber) => {
             return () => subscriber();
        });
      }, []);

1
返回一个清理函数,该函数仅在订阅者准备就绪后才使用它,甚至可以在调用getItem完成之前调用以防止订阅者的创建。 - Bergi
@Bergi如果在getItem完成之前调用,会防止创建订阅者,这就是我使用异步函数和等待从AsyncStorage获取结果的原因。我面临的问题是如何返回订阅者函数,因为该方法返回Promise。 - Vikum Dheemantha
1
你无法从 getData 的订阅函数中得到更好的承诺。请展示一下在 useEffect 中如何调用它。 - Bergi
我使用了 useEffect 编辑了代码。 - Vikum Dheemantha
1个回答

2

你不能将getData更改为立即返回清理函数,你应该使用

useEffect(() => {
    let subscriber = () => {};
    getData().then(sub => {
         subscriber = sub;
    });
    return () => {
         subscriber();
    };
}, []);

或者

useEffect(() => {
    const subscriberPromise = getData();
    return async () => {
         const subscriber = await subscriberPromise;
         subscriber();
    };
}, []);

然而,当清理函数在承诺被实现之前被调用时(更不用说错误处理了...),这些都不是理想的选择。虽然第一个在这种情况下根本不起作用,但第二个仍然不必要地启动了订阅。为了正确地执行它,我建议使用以下方法:

useEffect(() => {
    let cleanup = () => {};
    AsyncStorage.getItem('@collection_name').then(collName => {
        if (!cleanup) return; // already cancelled
//      ^^^^^^^^^^^^^^^^^^^^
        cleanup = shopReference.collection(collName).onSnapshot((snap) => {
            let menuList = [];
            snap.forEach((doc) => {
                menuList.push({
                    id: doc.id,
                    ...doc.data(),
                });
            });
            setMenu(menuList);
        });
    });

    return () => {
         cleanup();
         cleanup = null;
    };
}, []);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接