How to pass variables to a GraphQL query?

henk picture henk · Dec 31, 2016 · Viewed 13.9k times · Source

Lets assume I have the following GraphQL query in React-Apollo

const CurrentUserForLayout = gql`
  query CurrentUserForLayout($avatarID: Int!) {
    currentUser {
      avatar_url(avatarID: $avatarID)
    }
  }
`;

const ProfileWithData = graphql(CurrentUserForLayout, {
  options: { variables: { avatarID: 1 } },
})(Profile);

Now if I want to let my React component Profile change the avatarID,

how would I do that?

I am new to React and GraphQl and I do not really understand the connection here:

graphql(CurrentUserForLayout, {
      options: { variables: { avatarID: 1 } },
    })(Profile);

Do I really need another parent component around ProfileWithData to pass another avatarID to the query? But what if the ID is manipulated by the Profile component, how do I let the Parent component know about that?

Answer

Robin Wieruch picture Robin Wieruch · Apr 22, 2018

Before Apollo Client 2.1

Before Apollo Client 2.1, when you had to use Higher-Order Components (HOCs), there were two solutions which were pointed out correctly by henk in the other answer. I try to summarize them briefly:

  • Since the HOC has only access to the props, you need to introduce the changing variable in your parent component. For instance, the parent component could have local state management in place for the changing variable with React's setState() method. Once the variable changes in the local state, it can be passed down to your other component which is enhanced by the HOC. The HOC has access to the props and can execute the query with a changed variable. Hint: If you don't want to introduce another component in between to change the local state, recompose's withState HOC might be a great choice.

  • The second way, but maybe less elegant way, because it shifts your code from declarative to imperative, would be to use the withApollo() HOC which gives you access to the Apollo Client instance as prop. Having this instance in your React component, you can call client.query({ query: ..., variables: ... }) in one of your component's class methods (e.g. onChange handler) once your variable changes.

Since Apollo Client 2.1

Since Apollo Client 2.1 you can use the new Query (and Mutation) component. The HOC still exists though, but isn't advertised by the documentation anymore. However, the new Query (and Mutation) component is used within your component. It uses React's render props pattern.

import React from "react";
import { Query } from "react-apollo";
import gql from "graphql-tag";

const GET_TODOS = gql`
  {
    todos {
      id
      type
    }
  }
`;

const Todos = () => (
  <Query query={GET_TODOS}>
    {({ loading, error, data }) => {
      if (loading) return <p>Loading...</p>;
      if (error) return <p>Error :(</p>;

      return data.todos.map(({ id, type }) => (
        <p key={id}>{type}</p>
      ));
    }}
  </Query>
);

By shifting this control flow inside of the component rather than having it only co-located with a HOC, you can pass the changing prop as variable to your Query (and Mutation) component.

import React from "react";
import { Mutation } from "react-apollo";
import gql from "graphql-tag";

const TOGGLE_TODO = gql`
  mutation ToggleTodo($id: Int!) {
    toggleTodo(id: $id) {
      id
      completed
    }
  }
`;

const Todo = ({ id, text }) => (
  <Mutation mutation={TOGGLE_TODO} variables={{ id }}>
    {(toggleTodo, { loading, error, data }) => (
      <div>
        <p onClick={toggleTodo}>
          {text}
        </p>
        {loading && <p>Loading...</p>}
        {error && <p>Error :( Please try again</p>}
      </div>
    )}
  </Mutation>
);

If you want to learn Apollo Client in React, checkout this extensive tutorial. Note: Code Snippets are taken from the Apollo Client 2.1 release blog post.