Do you want to request a feature or report a bug?
Feature
What is the current behavior?
N/A
What is the expected behavior?
When I configure the store and add the reactReduxFirebase enhancer, I expect a way to specify a callback that can be called in certain events. An example could be when a document mapped with firestoreConnect is received, and the @@reduxFirestore/LISTENER_RESPONSE action is dispatched. But ideally, a way to get a callback for each of the @@reduxFirestore actions that are dispatched.
An example could be
const store = createStore(
reducer,
initialValue,
composeWithDevTools(
reactReduxFirebase(firebase, {
userProfile: 'users',
useFirestoreForProfile: true,
attachAuthIsReady: true,
eventHandlers: {
listenerResponse: (response) => {
MyModule.handleResponse(response)
SomeOtherModule.handleResponse(response)
}
}
}),
reduxFirestore(firebase)
)
)
My use case:
I have a firestore database with documents, and each screen in the app is mapped to one or more firestore documents. The documents consist of data imported from 3rd party APIs and have an expiration timestamp on them.
When I get a response after mapping a document, I want to check if the document exists payload.data !== null and that the document has not expired payload.data.expires > Date.now() - if one of these are true, I want to invoke a firebase cloud function that (re)imports the requested document.
I've made a workaround that solves the problem for now, by making a middleware which gives me the dispatched actions, and then switch for the actions that originate from redux-firestore and react-redux-firebase
import { reduxFirestore, actionTypes } from 'redux-firestore'
function handleListenerResponse(action) {
if (action.payload.data === null) {
console.log('EMPTY PAYLOAD - should request a document import', action)
}
}
function handleAction(action: any) {
switch (action.type) {
case actionTypes.LISTENER_RESPONSE: {
handleListenerResponse(action)
break
}
default:
break
}
}
const observeActions = store => next => action => {
handleAction(action)
return next(action)
}
export const getStore = () => {
const store = createStore(
reducer,
initialValue,
composeWithDevTools(
reactReduxFirebase(firebase, {
userProfile: 'users',
useFirestoreForProfile: true,
attachAuthIsReady: true,
}),
reduxFirestore(firebase),
applyMiddleware(observeActions)
)
)
return store
I had the same situation before but with facebook API. I thought it was too redundant to use cloud functions to update those API into firestore, but instead directly pull data from facebook API into redux with duplication and expiration checking logic. So I ended up with writing an entire library of react-redux-facebook(not publish yet).
@illuminist Glad to hear! I'm sure there is some way that we could work the logic in.
@esbenvb Thanks for posting your solution!
It seems like the updated state is not available, when my middleware is activated. When my observeActions middleware gets an action path defined as action.meta.storeAs, the state does not contain data at that path. It seems like my custom middleware is run before the action results are saved in the database.
I wonder why that happens? Can I do anything, like adjusting the order in the compose/applyMiddleware below?
export const getStore = () => {
const store = createStore(
persistedReducer,
initialValue,
composeWithDevTools(
reactReduxFirebase(firebase, {
userProfile: 'users',
useFirestoreForProfile: true,
}),
reduxFirestore(firebase),
applyMiddleware(
thunk.withExtraArgument({ getFirebase, getFirestore }),
observeActions
)
)
)
return store
}
export const observeActions = (store: MiddlewareAPI) => (next: Dispatch) => (
action: Action
) => {
handleAction(action, store)
return next(action)
}
If I go to handleAction() and get the current state from the store, the storeAs path from the action is not present in state.firestore.data
State is updated after you call next
https://redux.js.org/advanced/middleware#attempt-3-monkeypatching-dispatch
State is updated _after_ you call
next
Thank you @illuminist
It works now with
export const observeActions = (store: MiddlewareAPI) => (next: Dispatch) => (
action: Action
) => {
next(action)
handleAction(action, store)
}
I think you need to also return the result from next or else you will break middleware chain.
Going to close since it appears that a the proposed workaround suffices. Thanks for reaching out