Started playing around with createAsyncThunk
for learning purpose, decided to implement a shopping cart with firebase firestore but I ran into problems when trying to implement pagination in my react app.
How should I return the last visible state into my redux state during the initial load and subsequent load (infinite loading)
I am basing on code from redux tutorial sandbox :https://codesandbox.io/s/github/reduxjs/redux-essentials-example-app/tree/checkpoint-3-postRequests/?from-embed, but instead of connecting to a fake api, I am using firebase firestore.
Code to fetch product from firestore : ProductSlice.js
const InitialState = {
products : [],
status: 'idle',
error: null,
last: null, //to hold lastVisible when fetching data from firestore
}
export const fetchProducts = createAsyncThunk(types.RECEIVE_PRODUCTS, async (limit) => {
const resp = await fire_base_product.firestore()
.collection(collection_name).orderBy('id').limit(limit)
let result = resp.get().then((querySnapshot) => {
const lastVisible = querySnapshot.docs[querySnapshot.docs.length - 1] //how set this to redux state
const products = querySnapshot.docs.map((doc)=> {
return { ...doc.data()}
})
return {
products: products,
lastVisible: lastVisible
};
})
return result;
}
I am not quite sure on how to set this lastVisible
data back into redux state, is it possible to do that with reference?
#Edit:
Tried to return both product list and last visible as an array and assign lastVisible in createSlice as stated below:
const productSlice = createSlice({
name:'products',
initialState:
reducers: {},
extraReducers:{
[fetchProducts.fulfilled]: (state, action) => {
state.products = state.products.concat(action.payload.products)
state.last = action.payload.lastVisible // this causes call stack error
}
}
});
With the above coding, two error will be reported if I run react app,
I then tried to add middleware serializableCheck during create configuration as below:
export default configureStore({
middlleware: getDefaultMiddlleWare({
serializableCheck: {
//ignore action type
ignoredActions : ['RECEIVE_PRODUCTS/fulfilled']
// ignore path
ignoredPath: ['products.last']
}
}),
... // reducer codes
})
Even though now I have dismissed the first error, call stack exceeded still exists. Does anyone knows why this is happening ? Feel free to discuss if there is any workaround on this. Thanks.
Similar approach works when using context but does not work when using redux. Do I need to wrap return in promise as suggested in Firebase Unhandled error RangeError: Maximum call stack size exceeded ?
Did not managed to find a way to save lastVisible
, but I found a workaround by just keeping track of the last retrieve id of my firestore data by saving it into redux state.
export const fetchProducts = createAsyncThunk(types.RECEIVE_PRODUCTS, async (limit) => {
const resp = await fire_base_product.firestore()
.collection(collection_name).orderBy('id').limit(limit)
let result = resp.get().then((querySnapshot) => {
var lastVisible = limit - 1; //only keep track of ID so we can avoid saving un-serialize coded
const products = querySnapshot.docs.map((doc)=> {
return { ...doc.data()}
})
return {
products: products,
lastVisible: lastVisible
};
})
return result;
}
And when during fetch of additional data we can then access the state by using getState() as below:
export const fetchMoreProducts = createAsyncThunk(types.LOAD_MORE_PRODUCTS, async (limit, {getState}) => {
const last = getState().products.last
var newProducts = await firebase_product.firestore()
.collection('store_products').orderBy('id')
.startAfter(last).limit(limit)
const result = newProducts.get().then((querySnapshot) => {
var lastVisible = last + limit;
const products = querySnapshot.docs.map((doc) => {
return { ...doc.data() }
})
return {
products : products,
lastVisible: lastVisible
}
})
// return retrieved data from firebase
return result
})
But doing this, I could skip the serialization check config all together as well. Not sure if this is the correct way, but this is how I got pagination working. Feel free to let me know if there is other way to approach this.