API: graphql container

graphql(query, [config])(component)

1
import { graphql } from 'react-apollo';

The graphql() function is the most important thing exported by react-apollo. With this function you can create higher-order components that can execute queries and update reactively based on the data in your Apollo store. The graphql() function returns a function which will “enhance” any component with reactive GraphQL capabilities. This follows the React higher-order component pattern which is also used by react-redux’s connect function.

The graphql() function may be used like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function TodoApp({ data: { todos } }) {
return (
<ul>
{todos.map(({ id, text }) => (
<li key={id}>{text}</li>
))}
</ul>
);
}
export default graphql(gql`
query TodoAppQuery {
todos {
id
text
}
}
`)(TodoApp);

You may also define an intermediate function and hook up your component with the graphql() function like this:

1
2
3
4
5
6
7
8
// Create our enhancer function.
const withTodoAppQuery = graphql(gql`query { ... }`);
// Enhance our component.
const TodoAppWithData = withTodoAppQuery(TodoApp);
// Export the enhanced component.
export default TodoAppWithData;

Alternatively, you can also use the graphql() function as a decorator on your React class component.

If so your code may look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@graphql(gql`
query TodoAppQuery {
todos {
id
text
}
}
`)
export default class TodoApp extends Component {
render() {
const { data: { todos } } = this.props;
return (
<ul>
{todos.map(({ id, text }) => (
<li key={id}>{text}</li>
))}
</ul>
);
}
}

The graphql() function will only be able to provide access to your GraphQL data if there is a <ApolloProvider/> component higher up in your tree to provide an ApolloClient instance that will be used to fetch your data.

The behavior of your component enhanced with the graphql() function will be different depending on if your GraphQL operation is a query, a mutation, or a subscription. Go to the appropriate API documentation for more information about the functionality and available options for each type.

Before we look into the specific behaviors of each operation, let us look at the config object.

config

The config object is the second argument you pass into the graphql() function, after your GraphQL document. The config is optional and allows you to add some custom behavior to your higher order component.

1
2
3
4
export default graphql(
gql`{ ... }`,
config, // <- The `config` object.
)(MyComponent);

Lets go through all of the properties that may live on your config object.

config.options

config.options is an object or a function that allows you to define the specific behavior your component should use in handling your GraphQL data.

The specific options available for configuration depend on the operation you pass as the first argument to graphql(). There are options specific to queries and mutations.

You can define config.options as a plain object, or you can compute your options from a function that takes the component’s props as an argument.

Example:

1
2
3
4
5
export default graphql(gql`{ ... }`, {
options: {
// Options go here.
},
})(MyComponent);
1
2
3
4
5
export default graphql(gql`{ ... }`, {
options: (props) => ({
// Options are computed from `props` here.
}),
})(MyComponent);

config.props

The config.props property allows you to define a map function that takes your props including the props added by the graphql() function (props.data for queries and props.mutate for mutations) and allows you to compute a new props object that will be provided to the component that graphql() is wrapping.

The function you define behaves almost exactly like mapProps from Recompose providing the same benefits without the need for another library.

config.props is most useful when you want to abstract away complex functions calls into a simple prop that you can pass down to your component.

Another benefit of config.props is that it also allows you to decouple your pure UI components from your GraphQL and Apollo concerns. You can write your pure UI components in one file and then keep the logic required for them to interact with the store in a completely different place in your project. You can accomplish this by your pure UI components only asking for the props needed to render and config.props can contain the logic to provide exactly the props your pure component needs from the data provided by your GraphQL API.

Example:

This example uses props.data.fetchMore.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default graphql(gql`{ ... }`, {
props: ({ data: { fetchMore } }) => ({
onLoadMore: () => {
fetchMore({ ... });
},
}),
})(MyComponent);
function MyComponent({ onLoadMore }) {
return (
<button onClick={onLoadMore}>
Load More!
</button>
);
}

config.skip

If config.skip is true then all of the React Apollo code will be skipped entirely. It will be as if the graphql() function were a simple identity function. Your component will behave as if the graphql() function were not there at all.

Instead of passing a boolean to config.skip, you may also pass a function to config.skip. The function will take your components props and should return a boolean. If the boolean returns true then the skip behavior will go into effect.

config.skip is especially useful if you want to use a different query based on some prop. You can see this in an example below.

Example:

1
2
3
export default graphql(gql`{ ... }`, {
skip: props => !!props.skip,
})(MyComponent);

The following example uses the compose() function to use multiple graphql() enhancers at once.

1
2
3
4
5
6
7
8
9
10
export default compose(
graphql(gql`query MyQuery1 { ... }`, { skip: props => !props.useQuery1 }),
graphql(gql`query MyQuery2 { ... }`, { skip: props => props.useQuery1 }),
)(MyComponent);
function MyComponent({ data }) {
// The data may be from `MyQuery1` or `MyQuery2` depending on the value
// of the prop `useQuery1`.
console.log(data);
}

config.name

This property allows you to configure the name of the prop that gets passed down to your component. By default if the GraphQL document you pass into graphql() is a query then your prop will be named data. If you pass a mutation then your prop will be named mutate. While appropriate these default names collide when you are trying to use multiple queries or mutations with the same component. To avoid collisions you may use config.name to provide the prop from each query or mutation HOC a new name.

Example:

This example uses the compose function to use multiple graphql() HOCs together.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default compose(
graphql(gql`mutation (...) { ... }`, { name: 'createTodo' }),
graphql(gql`mutation (...) { ... }`, { name: 'updateTodo' }),
graphql(gql`mutation (...) { ... }`, { name: 'deleteTodo' }),
)(MyComponent);
function MyComponent(props) {
// Instead of the default prop name, `mutate`,
// we have three different prop names.
console.log(props.createTodo);
console.log(props.updateTodo);
console.log(props.deleteTodo);
return null;
}

config.withRef

By setting config.withRef to true you will be able to get the instance of your wrapped component from your higher-order GraphQL component using a getWrappedInstance method available on the instance of your higher-order GraphQL component.

You may want to set this to true when you want to call functions or get access to properties that are defined on your wrapped component’s class instance.

Below you can see an example of this behavior.

Example:

This example uses the React ref feature.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class MyComponent extends Component {
saySomething() {
console.log('Hello, world!');
}
render() {
// ...
}
}
const MyGraphQLComponent = graphql(
gql`{ ... }`,
{ withRef: true },
)(MyComponent);
class MyContainerComponent extends Component {
render() {
return (
<MyGraphQLComponent
ref={component => {
assert(component.getWrappedInstance() instanceof MyComponent);
// We can call methods on the component class instance.
component.saySomething();
}}
/>
);
}
}

config.alias

By default the display name for React Apollo components is Apollo(${WrappedComponent.displayName}). This is a pattern used by most React libraries that make use of higher order components. However, it may get a little confusing when you are using more then one higher order components and you look at the React Devtools.

To configure the name of your higher order component wrapper, you may use the config.alias property. So for example, if you set config.alias to 'withCurrentUser' your wrapper component display name would be withCurrentUser(${WrappedComponent.displayName}) instead of Apollo(${WrappedComponent.displayName}).

Example:

This example uses the compose function to use multiple graphql() HOCs together.

1
2
3
4
export default compose(
graphql(gql`{ ... }`, { alias: 'withCurrentUser' }),
graphql(gql`{ ... }`, { alias: 'withList' }),
)(MyComponent);

compose(...enhancers)(component)

1
import { compose } from 'react-apollo';

For utility purposes, react-apollo exports a compose function. Using this function you may cleanly use several component enhancers at once. Including multiple graphql(), withApollo(), or Redux connect() enhancers. This should clean up your code when you use multiple enhancers. Redux also exports a compose function, and so does Recompose so you may choose to use the function from whichever library feels most appropriate.

An important note is that compose() executes the last enhancer first and works its way backwards through the list of enhancers. To illustrate calling three functions like this: funcC(funcB(funcA(component))) is equivalent to calling compose() like this: compose(funcC, funcB, funcA)(component). If this does not make sense to you consider using flowRight() from Lodash which otherwise has the same behavior.

Example:

1
2
3
4
5
6
export default compose(
withApollo,
graphql(`query { ... }`),
graphql(`mutation { ... }`),
connect(...),
)(MyComponent);
Edit on GitHub