React-apollo: Dynamically create different queries based on parents props

Created on 5 Oct 2017  路  6Comments  路  Source: apollographql/react-apollo

We were facing some problems with our own HOC we've written. This HOC does nothing special except to wrap a Component with graphql(). The only special was that depending on the props (from router) the Query changes. The problem here is now that wrapping the component with a new graphql() (so creating a new component) will cause remounting of the Component.

I know that there is a way to pass parameters dynamically based on the properties down to the query but in our case we need to execute different queries based on the props. Example:

// ...
const query = BuildQueryBasedOnProps(this.props);
ComponentWithApollo = graphql(gql(BuildQueryBasedOnProps))(ComponentToWrap);
// ...
return (
  <ComponentWithApollo
    {...this.props}
  />
);

So in our case we need to execute sometimes the query
query myQuery { getPerson() {} }

and sometimes
query mySecondQuery { getAnimal() {} }
.

So we were now thinking of wrapping our Component multiple times with graphql with any possible query. But how to tell graphql if it should execute it or if not ? Is there a way ? Something like:

export default compose(
  graphql(QUERY_GET_PERSON, {
    execute(props) {
      return props.doPerson;
    }
  }),
  graphql(QUERY_GET_ANIMAL, {
    execute(props) {
      return props.doAnimal;
    }
  })
)(MyApp);

Or maybe our thoughts about this issues are going into a wrong direction ? Is there a recommended way to create dynamic queries without "re-wrapping" the connected component ?

Version

  • react-apollo@^1.4.16

Most helpful comment

Something like this?

// @flow

import React from 'react'
import gql from 'graphql-tag'

import { getModelFields } from 'preset/utils'

const withItemQuery = (WrappedComponent: any) => (props: any) => {
  const { model: { fields, itemQueryName } } = props
  const query = gql(`
    query ${itemQueryName}($id: ID!) {
      item: ${itemQueryName}(id: $id) ${getModelFields(fields)}
    }
  `)
  return (
    <WrappedComponent
      itemQuery={query}
      itemQueryName={itemQueryName}
      {...props}
    />
  )
}

export default withItemQuery

All 6 comments

@TimoRuetten why did you close this?

Well, this was closed but Ill reply anyway in case someone stumbles upon this.
You can actually write a skip condition in yout graphql() second argument.

const ProfileWithData = graphql(CurrentUserForLayout, {
  skip: (ownProps) => !ownProps.authenticated,
})(Profile);

Apollo documentation:
https://www.apollographql.com/docs/react/basics/queries.html#graphql-skip

The skip is not what the first issue asks.

Something like this?

// @flow

import React from 'react'
import gql from 'graphql-tag'

import { getModelFields } from 'preset/utils'

const withItemQuery = (WrappedComponent: any) => (props: any) => {
  const { model: { fields, itemQueryName } } = props
  const query = gql(`
    query ${itemQueryName}($id: ID!) {
      item: ${itemQueryName}(id: $id) ${getModelFields(fields)}
    }
  `)
  return (
    <WrappedComponent
      itemQuery={query}
      itemQueryName={itemQueryName}
      {...props}
    />
  )
}

export default withItemQuery

I wanted to do this exact same thing, so I open sourced a simple function that handles this.

https://www.npmjs.com/package/react-apollo-dynamic-query

Hilariously enough, I wrote the documentation before seeing @TimoRuetten's original comment, but they both go through the same scenario of getting a "person" and an "animal".

I found using the @include and @skip directives solved this for me. https://graphql.org/learn/queries/#directives

Was this page helpful?
0 / 5 - 0 ratings