Вернуть функцию очистки, возвращающую форму асинхронной функции в userEffect в реакции Native

Я использую метод firebase onSnapshot для использования данных в реальном времени в собственном приложении для реагирования и написал отдельную асинхронную функцию для прослушивания 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 для получения идентификатора коллекции, мне нужно поместить его в асинхронную функцию. Я не могу вернуть ответ метода getData(), так как он возвращает обещание вместо функции подписчика. Какое лучшее решение для решения этой проблемы?

РЕДАКТИРОВАТЬ

Я попробовал все следующие методы для вызова функции внутри функции 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();
        });
      }, []);

🤔 А знаете ли вы, что...
JavaScript позволяет создавать расширения для веб-браузеров, улучшая их функциональность.


3
189
1

Ответ:

Решено

Вы не можете изменить 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;
    };
}, []);