notrab
notrab

Reputation: 51

Dispatch Redux action after React Apollo query returns

I'm using React Apollo to query all records in my datastore so I can create choices within a search filter.

The important database model I'm using is Report.

A Report has doorType, doorWidth, glass and manufacturer fields.

Currently when the query responds, I'm passing allReports to multiple dumb components which go through the array and just get the unique items to make a selectable list, like so..

const uniqueItems = []

items.map(i => {
  const current = i[itemType]

  if (typeof current === 'object') {
    if (uniqueItems.filter(o => o.id !== current.id)) {
      return uniqueItems.push(current)
    }
  } else if (!uniqueItems.includes(current)) {
    return uniqueItems.push(current)
  }

  return
})

Obviously this code isn't pretty and it's a bit overkill.

I'd like to dispatch an action when the query returns within my SidebarFilter components. Here is the query...

const withData = graphql(REPORT_FILTER_QUERY, {
  options: ({ isPublished }) => ({
    variables: { isPublished }
  })
})

const mapStateToProps = ({
  reportFilter: { isPublished }
  // filterOptions: { doorWidths }
}) => ({
  isAssessment
  // doorWidths
})

const mapDispatchToProps = dispatch =>
  bindActionCreators(
    {
      resetFilter,
      saveFilter,
      setDoorWidths,
      handleDoorWidthSelect
    },
    dispatch
  )

export default compose(connect(mapStateToProps, mapDispatchToProps), withData)(
  Filter
)

The Redux action setDoorWidths basically does the code above in the SidebarFilter component but it's kept in the store so I don't need to re-run the query should the user come back to the page.

It's very rare the data will update and the sidebar needs to change.

Hopefully there is a solution using the props argument to the graphql function. I feel like the data could be taken from ownProps and then an action could be dispatched here but the data could error or be loading, and that would break rendering.

Edit:

Query:

query ($isPublished: Boolean!){
  allReports(filter:{
    isPublished: $isPublished
  }) {
  id
  oldId
  dbrw
  core
  manufacturer {
    id
    name
  }
  doorWidth
  doorType
  glass
  testBy
  testDate
  testId
  isAssessment
  file {
    url
  }
  }
}

Upvotes: 5

Views: 855

Answers (2)

Adam Donahue
Adam Donahue

Reputation: 1658

While this answer addresses the specific issue of the question, the more general question -- where to dispatch a Redux action based on the result of a query -- remains unclear. There does not, as yet, seem to be a best practice here.

Upvotes: 4

Daniel Rearden
Daniel Rearden

Reputation: 84667

It seems to me that, since Apollo already caches the query results in your store for you (or a separate store, if you didn't integrate them), it would be redundant to dispatch an action that would also just store the data in your store.

If I understood your question correctly, your intent is to filter the incoming data only once and then send the result down as a prop to the component's stateless children. You were on the right track with using the props property in the graphql HOC's config. Why not just do something like this:

const mapDataToProps = ({ data = {} }) => {
  const items = data
  const uniqueItems = []

  // insert your logic for filtering the data here

  return { uniqueItems } // or whatever you want the prop to be called
}

const withData = graphql(REPORT_FILTER_QUERY, {
  options: ({ isPublished }) => ({
    variables: { isPublished }
  }),
  props: mapDataToProps,
})

The above may need to be modified depending on what the structure of data actually looks like. data has some handy props on it that can let you check for whether the query is loading (data.loading) or has errors (data.error). The above example already guards against sending an undefined prop down to your children, but you could easily incorporate those properties into your logic if you so desired.

Upvotes: 2

Related Questions