React-redux-firebase: Event handler callbacks when getting responses etc.

Created on 27 Apr 2019  路  8Comments  路  Source: prescottprue/react-redux-firebase

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.

All 8 comments

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

Was this page helpful?
0 / 5 - 0 ratings