40

I have a queries file that looks like this:

import {gql} from 'react-apollo';

const queries = {
  getApps: gql`
    {
      apps {
        id
        name
      }
    }
  `,
  getSubjects: gql`
    {
      subjects {
        id
        name
      }
    }
  `
};

export default queries;

I then import this file to my React component:

import React, {Component} from 'react'
import queries from './queries'

class Test extends Component {
...
}

export default graphql(queries.getSubjects)(graphql(queries.getApps)(Test));

This will only get data for one of the queries (getApps) and not both. If I do one at a time so that it looks like this:

export default graphql(queries.getSubjects)(Test);

then it works but I don't have my other query. Yes, I have tested both separately and they work. How do I get it so that both queries show up in my props.data?

Eesa
  • 2,288
  • 2
  • 24
  • 46
user2465134
  • 6,278
  • 5
  • 23
  • 39

10 Answers10

45

My preferred way is to use the compose functionality of the apollo client (docu).

EDIT: If you have more than one query you should name them.

So in your case, it could look like this:

import React, {Component} from 'react'
import queries from './queries'
import { graphql, compose } from 'react-apollo';

class Test extends Component {
...

  render() {
    ...
    
    console.log(this.props.subjectsQuery, this.props.appsQuery); // should show both 
    
    ...
  }
}

export default compose(
   graphql(queries.getSubjects, {
      name: "subjectsQuery"
   }),
   graphql(queries.getApps, {
      name: "appsQuery"
   }),
)(Test);
Locco0_0
  • 2,768
  • 5
  • 24
  • 32
12

IMHO, one of the most neat solutions is described in the Apollo Client React implementation.
The basic idea is to wrap your queries into nested Query components. Using closure functions as component children makes it handy to delegate the results of one query down into another query and so on.

 const QueryOne = gql`
  query One {
    one
  }
`;

const QueryTwo = gql`
  query Two {
    two
  }
`;

const NumbersWithData = () => (
  <Query query={QueryOne}>
    {({ loading: loadingOne, data: { one } }) => (
      <Query query={QueryTwo}>
        {({ loading: loadingTwo, data: { two }}) => {
          if (loadingOne || loadingTwo) return <span>loading...</span>
          return <h3>{one} is less than {two}</h3>
        }}
      </Query>
    )}
  </Query>
);
theVoogie
  • 1,320
  • 1
  • 16
  • 22
  • I don't get the downvote on this as [1] describes that these nested `` structure will be the result of using `compose`. And so it may be justified to use it in cases where `compose` appears unwieldy or you just don't want to build another component. [1]: https://www.apollographql.com/docs/react/react-apollo-migration.html#compose-to-render-composition – Jakob Runge Aug 02 '18 at 07:55
  • note this _looks_ like QueryTwo will run only after QueryOne returns, but note that QueryTwo is not waiting for loadingOne to turn false, so they will in fact run in parallel. Unless you want to use QueryOne results as parameters to QueryTwo, in that case you can wait for loadingOne before rendering QueryTwo – Hoffmann Sep 21 '18 at 13:06
  • 24
    I don't agree that this is 'neat', it looks like a nested-render-props-hell in the making. – Josh Pittman Nov 07 '18 at 08:32
  • 1
    I do agree that this is neat because it lets you build composable components with their own queries. For example, you can have a parent component that handles paging lists of categories. It can be agnostic about which children get rendered. So, you can have a screen that lists authors and renders the author details for each author. The first component queries authors and the second author details. You can use the same top component to query articles and a different one to query article details as a child. It's really very tidy! However, evidently, it is hard to have one loading status. – D. Patrick Mar 08 '19 at 20:56
10

If you don't want to reuse any of those queries independently, why not make a single request by combining both queries in one i.e:

const combinedQueries = gql`
{
  apps {
    id
    name
  }
  subjects {
    id
    name
  }
}
`

and then you can use it in your component

import React, {Component} from 'react'
import combinedQueries from './combinedQueries'

class Test extends Component {
   ...
   render() {
     ...
     if(!this.props.combinedQueries.loading) {
       console.log(this.props.combinedQueries.apps);
       console.log(this.props.combinedQueries.subjects);
     }
     ...
   }
}

export default graphql(combinedQueries, {name: 'combinedQueries'})(Test);
Eesa
  • 2,288
  • 2
  • 24
  • 46
7

I'm using react-adopt to make this. It's really simple and keep our code clean.

Simple example:

import { adopt } from 'react-adopt';

...
render() {
  const Composed = adopt({
    first: ({ render }) => <Query query={FIRST_QUERY}>{ render }</Query>,
    second: ({ render }) => <Query query={SECOND_QUERY}>{ render }</Query>
  });

  return (
    <Composed>
      ({ first, second }) => {
        console.log('first', first)
        console.log('second', second)

        // validations (loading, error)

        return (
          <div>Your JSX</div>
        )
      }
    </Composed>
  )
}
...

There are a lot of examples using

const Composed = adopt({
  first: <Query query={FIRST_QUERY} />,
  second: <Query query={SECOND_QUERY} />
});

Be careful with <Query> component, It needs a children, otherwise, it will have the following error:

Warning: Failed prop type: The prop children is marked as required in Query, but its value is undefined.

To avoid the previous warning, I have found a possible solution:

first: ({ render }) => <Query query={FIRST_QUERY}>{ render }</Query>

Hope it helps you!

slorenzo
  • 3,386
  • 2
  • 27
  • 44
  • It would be a lot better to understand if you applied the Query components rather than just console.log them IMO. – Isaac Pak Feb 27 '19 at 19:51
5

For Apollo 2.x: you can use react-adopt to compose the Queries and Mutations into a single level. (That lib will compose any components with render props, e.g. the React Context API.)

https://github.com/pedronauck/react-adopt

Freewalker
  • 3,608
  • 1
  • 30
  • 52
3

The Emergence of Apollo Client useQuery Hooks; have changed everything. If you are reading this in 2020 or beyond; I am pretty much sure that you likely be using Apollo client useQuery hook. You can call useQuery Hook as many times as you want to execute the both queries. You can learn more about useQuery hooks in its official documentation https://www.apollographql.com/docs/react/data/queries/ I find it so useful in my recent project. E.g

const queries = {
  getApps: gql`
    {
      apps {
        id
        name
      }
    }
  `,

  getSubjects: gql`
    {
      subjects {
        id
        name
      }
    }
  `
};

const { loading, error, data } = useQuery(queries);

const { loading:getSubjectsLoading, error:getSubjectsError, data:getSubjects } = useQuery(getSubjects);

if (loading || getSubjectsLoading) return "Loading...";
if (error || getSubjectsError ) return <p>Error :(</p>;


console.log(data);
console.log(getSubjects);
Emeka Augustine
  • 531
  • 8
  • 13
  • Hey Emeka - as far as I can tell, this will still make multiple network POST calls to the graphql endpoint. Is this the same in your experience? – Chris Sargent Jul 15 '20 at 15:52
  • Yes; but that is what the question is all about. He want to make multiple queries. This will make multiple network calls to the graphql server. – Emeka Augustine Jul 15 '20 at 20:42
  • Okay, yes, for some reason I though the OP was looking for a way do it using just one call. (that's what I was searching for). I guess the only way is to build a brand new query. Thanks. – Chris Sargent Jul 16 '20 at 18:07
1

According to this Link, to use compose() you need to follow these steps:

1- install "recompose" package using npm i recompose

2- import package using import { compose } from "recompose";

3- use it in the form of:

export default compose(
  graphql(Query1, { alias: "Query1" }),
  graphql(Query2, { alias: "Query2" })
)(Test);

documentation : https://www.apollographql.com/docs/react/api/react-apollo/

sina
  • 1,117
  • 12
  • 21
0

Another way around this is to use the props option.

export default compose(
  graphql(QUERY_2, {
    props: ({ data }) => ({ ...data }),
  }),
  graphql(QUERY_1, {
    props: ({ data }) => ({ ...data, myCustomAttribute: data.foo }),
  }),
)(Component);

I've found that this approach is a bit nicer for my use case.

Here is a link to the docs: https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-config-props

user3812429
  • 555
  • 1
  • 4
  • 7
0

Since, compose has been removed from apollo, there's an alternative library called lodash. The method {flowRight} acts in the same way as compose. Just follow the steps:-

  1. npm i -s lodash

  2. import {flowRight} from 'lodash'

  3. Exchange the usage of compose with flowRight, all the other code will work the same.

-1
  const { loading: cat_loading, cat_error, data: cat_data } = useQuery(categoriesAllQuery)
  const { loading: prod_loading, prod_error, data: prod_data } = useQuery(productsAllQuery)

  if (cat_loading || prod_loading) return <p>Loading ... </p>
  const { categoriesAll } = cat_data
  const { productsAll } = prod_data