简体   繁体   中英

How do I load firebase data into react-redux asynchronously?

I am currently trying to load my product data into redux, but so far I cant seem to pass the product information returned from firestore into the reducer.

Index.js -> load first 10 products from firestore soon after store was created.

store.dispatch(getAllProducts)

action/index.js

import shop from '../api/shop'

const receiveProducts = products => ({
  type: types.RECEIVE_PRODUCTS
  products
})

const getAllProducts = () => dispatch => {
  shop.getProducts(products => {
     dispatch(receiveProducts)
  })
}

shop.js

import fetchProducts from './firebase/fetchProducts'

export default {
   getProducts: (cb) => cb(fetchProducts())
}

fetchProducts.js

const fetchProducts = async() => {
   const ProductList = await firebase_product.firestore()
      .collection('store_products').limit(10)

   ProductList.get().then((querySnapshot) => {
      const tempDoc = querySnapshot.docs.map((doc) => {
         return { id: doc.id, ...doc.data() }
      })
   }).catch(function (error) {
      console.log('Error getting Documents: ', error)
   })
}

In product reducers

const byId = (state={}, action) => {
   case RECEIVE_PRODUCTS: 
      console.log(action); <- this should be products, but it is now promise due to aysnc function return?
}

I can get the documents with no issues (tempDocs gets the first 10 documents without any issue.) but I am not able to pass the data back into my redux. If I were creating normal react app, I would add a loading state when retrieving the documents from firestore, do I need to do something similar in redux as well?

Sorry if the code seems messy at the moment.

fetchProducts is an async function so you need to wait for its result before calling dispatch . There are a few ways you could do this, you could give fetchProducts access to dispatch via a hook or passing dispatch to fetchProducts directly.

I don't quite understand the purpose of shop.js but you also could await fetchProducts and then pass the result of that into dispatch.

A generalized routine I use to accomplish exactly this:

const ListenGenerator = (sliceName, tableName, filterArray) => {
  return () => {
    //returns a listener function
    try {
      const unsubscribe = ListenCollectionGroupQuery(
        tableName,
        filterArray,
        (listenResults) => {
          store.dispatch(
            genericReduxAction(sliceName, tableName, listenResults)
          );
        },
        (err) => {
          console.log(
            err + ` ListenGenerator listener ${sliceName} ${tableName} err`
          );
        }
      );
      //The unsubscribe function to be returned includes clearing
      // Redux entry
      const unsubscriber = () => {
        //effectively a closure
        unsubscribe();
        store.dispatch(genericReduxAction(sliceName, tableName, null));
      };

      return unsubscriber;
    } catch (err) {
      console.log(
        `failed:ListenGenerator ${sliceName} ${tableName} err: ${err}`
      );
    }
  };
};

The ListenCollectionGroupQuery does what it sounds like; it takes a tableName , an array of filter/.where() conditions, and data/err callbacks.

The genericReduxAction pretty much just concatenates the sliceName and TableName to create an action type (my reducers de-construct action types similarly). The point is you can put the dispatch into the datacallback.

Beyond this, you simply treat Redux as Redux - subscribe, get, etc just as if the data were completely local.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM