等待 onSnapshot 获取数据

Posted

技术标签:

【中文标题】等待 onSnapshot 获取数据【英文标题】:wait for onSnapshot fetching data 【发布时间】:2021-11-26 21:59:23 【问题描述】:

我目前正在学习 React Native (Expo)。 我想使用 redux 和 react-native-firebase。

当我在我的应用启动时订阅 firebase (onSnapshot) 时,它会从 firebase 返回数据。但由于 onSnapchot 不返回承诺,我不能将它用于我的应用程序加载组件。 因此,我还需要从firebase中获取数据以防止应用闪烁。

结果是在我的应用启动时,我获取了两次数据。

所以我的问题是: 如何等待 onSnapshot 从 firebase 加载我的数据?

谢谢

const Manager = (props) => 
    //STATE
    const [init, setInit] = useState(false);

    //HOOKS
    const fetchData = useFetchData();
    useInitFirebaseSubscriptions();

    //FUNCTIONS
    async function onInit() 
        console.log('[MANAGER]: loading app...');
        await Promise.all([fetchData()]);
    
    function onFinishedInit() 
        console.log('[MANAGER]: ...app loading successfull!');
        setInit(true);
    

    //RETURN
    if (!init) 
        return <AppLoading startAsync=onInit onFinish=onFinishedInit onError=console.warn />;
     else 
        return props.children;
    
;
export default Manager;

//INITIAL FETCH BEFORE RENDERING
export function useFetchData() 
    const dispatch = useDispatch();

    return async function () 
        try 
            await firestore()
                .collection('users')
                .get()
                .then((querySnapshot) => dispatch(actions.fetch(querySnapshot)));
         catch (err) 
            console.log(err.message);
        
    ;

//INIT SUBSCRIPTIONS TO FIREBASE
export function useInitFirebaseSubscriptions() 
    const dispatch = useDispatch();

    useEffect(() => 
        console.log('[CONTROLLER]: subscribed to Firebase');
        const unsubscribe = firestore()
            .collection('users')
            .onSnapshot(
                (querySnapshot) => dispatch(action.fetch(querySnapshot)),
                (error) => console.log(error)
            );
        
        return () => 
            unsubscribe();
            console.log('[CONTROLLER]: unsubscribed from Firebase');
        ;
    , []);


[MANAGER]: loading app...
[MANAGER]: subscribed to Firebase
[USER_REDUCER]: fetched data
[USER_REDUCER]: fetched data
[MANAGER]: ...app loading successfull!

【问题讨论】:

如果useInitFirebaseSubscriptions 钩子也在获取数据,我看不出const fetchData = useFetchData(); 钩子的意义和逻辑。这里的用例是什么,你能澄清一下吗?如果我没看错的话,这似乎是一种奇怪的迂回方式来获得“加载”状态。 firebase.get 方法是异步的,所以我可以等待我的 useFetchData() Hook 完成,然后再渲染我的 UI。结果是当用户打开应用程序时,数据已经存在。 useInitFirebaseSubscriptions 不是异步的,所以我等不及要显示我的 UI 直到获取数据。我希望现在清楚 您不能等待数据填充到您的 redux 存储中吗?您无法从 useInitFirebaseSubscriptions 挂钩中分派开始/结束获取操作以指示您何时主动获取数据? 是的,这正是我的问题。如何等待来自 useInitFirebaseSubscriptions 的数据,或者如何知道 useInitFirebaseSubscriptions 是否仍在获取数据? 【参考方案1】:

我认为您可以通过在 redux 中添加一些“加载”状态来实现您的目标,以便您主动从 firebase 获取数据。添加特定于此数据获取/加载的 state 和 reducer case。

示例代码:

export function useInitFirebaseSubscriptions() 
  const dispatch = useDispatch();

  useEffect(() => 
    console.log('[CONTROLLER]: subscribed to Firebase');

    dispatch(action.startFetch()); // <-- dispatch starting data fetch

    const unsubscribe = firestore()
      .collection('users')
      .onSnapshot(
        (querySnapshot) => 
          dispatch(action.fetch(querySnapshot));
          dispatch(action.completedFetch()); // <-- done fetching
        ,
        (error) => 
          console.log(error);
          dispatch(action.completedFetch()); // <-- done fetching
        ,
      );
        
    return () => 
      unsubscribe();
      console.log('[CONTROLLER]: unsubscribed from Firebase');
    ;
  , []);
;

从redux store中选择加载状态,有条件地渲染加载UI,否则渲染传入的children。

const Manager = (props) => 
  const isFetchingData = useSelector(state => state.isFetchingData);

  if (isFetchingData) 
    return <AppLoadingIndicator />;
  
  return props.children; // *
;

* 通常,您可以在此处使用一些额外的条件渲染,具体取决于数据是否实际获取/返回并且只是空的,或者是否存在错误等...基本上提供了一些结果状态。

【讨论】:

伟大而简单的解决方案,谢谢。还没想过使用 onSnapshot 的回调 ? @sexpomicolon 是的,成功/失败回调在某种程度上是 Promise 链的 .then.catch 的同义词,并且由于没有等同于 .finally,因此不幸的是,您需要复制一些“已解决" 两个处理程序中的逻辑。

以上是关于等待 onSnapshot 获取数据的主要内容,如果未能解决你的问题,请参考以下文章

Firestore 抛出“onSnapshot() 需要 1 到 4 个参数”

Firebase firestore onSnapshot 在 useEffect 中无法正常工作

使用异步等待获取数据加载

等待位置数据时从服务器获取数据的功能放置在哪里

如何等待firebase数据获取完成并在react-native中获取不是承诺的值?

异步/等待无法获取 JSON 数据 [重复]