user2232355
user2232355

Reputation: 315

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.

Upvotes: 0

Views: 123

Answers (2)

LeadDreamer
LeadDreamer

Reputation: 3499

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.

Upvotes: 1

Zeke Hernandez
Zeke Hernandez

Reputation: 1336

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.

Upvotes: 2

Related Questions