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