user2465134
user2465134

Reputation: 9733

React Apollo - Make Multiple Queries

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?

Upvotes: 44

Views: 48238

Answers (11)

kenodek
kenodek

Reputation: 372

What about recursive React component?

export const QueriesExecutor = ({
    children,
    queries,
    variables,
    loading = false,
    ...rest
}) => {
    const lastQueryIndex = queries.length - 1;

    const {data, loading: _loading} = useQuery(
        queries[lastQueryIndex].query,
        {
            variables
        }
    );

    if (queries.length > 1) {
        return (
            <QueriesExecutor
                children={children}
                queries={queries.slice(0, lastQueryIndex)}
                variables={variables}
                loading={_loading}
                {...rest}
                {...data}
            />
        );
    }

    if (loading || _loading) return 'Loading...';

    return React.Children.only(
        React.cloneElement(children, {
            ...rest,
            ...data
        })
    );
};
  • queries: an array of queries that you want to run in parallel
  • base case: 1 query left, should render children
  • ...rest: used to collect query data from previous QueriesExecutor(s) until it reaches children

Upvotes: 0

Emeka Augustine
Emeka Augustine

Reputation: 931

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);

Upvotes: 5

SHREY DUBEY
SHREY DUBEY

Reputation: 1

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.

Upvotes: 0

endorphin
endorphin

Reputation: 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

Upvotes: -1

Sina Abedi
Sina Abedi

Reputation: 2381

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/

Upvotes: 1

Locco0_0
Locco0_0

Reputation: 3500

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);

Upvotes: 46

saeta
saeta

Reputation: 4238

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!

Upvotes: 8

syntax-punk
syntax-punk

Reputation: 4570

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>
);

Upvotes: 11

Freewalker
Freewalker

Reputation: 7315

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

Upvotes: 5

Eesa
Eesa

Reputation: 2859

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);

Upvotes: 13

user3812429
user3812429

Reputation: 655

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

Upvotes: 0

Related Questions