React Apollo components provide render prop patterns for GraphQL operations. These components use hooks internally and provide a declarative interface for data fetching.
Render prop component for executing GraphQL queries with declarative patterns.
/**
* Render prop component for GraphQL queries
* @param props - Query component props with render function
* @returns JSX element from children render function
*/
function Query<TData = any, TVariables = OperationVariables>(
props: QueryComponentOptions<TData, TVariables>
): JSX.Element | null;
interface QueryComponentOptions<TData = any, TVariables = OperationVariables>
extends QueryFunctionOptions<TData, TVariables> {
children: (result: QueryResult<TData, TVariables>) => JSX.Element | null;
query: DocumentNode;
}Usage Examples:
import React from "react";
import { Query } from "react-apollo";
import gql from "graphql-tag";
const GET_USERS = gql`
query GetUsers($limit: Int) {
users(limit: $limit) {
id
name
email
}
}
`;
function UserList() {
return (
<Query
query={GET_USERS}
variables={{ limit: 10 }}
fetchPolicy="cache-and-network"
>
{({ data, loading, error, refetch, fetchMore }) => {
if (loading) return <div>Loading users...</div>;
if (error) return <div>Error loading users: {error.message}</div>;
return (
<div>
<button onClick={() => refetch()}>Refresh</button>
<ul>
{data.users.map((user) => (
<li key={user.id}>
{user.name} - {user.email}
</li>
))}
</ul>
<button
onClick={() =>
fetchMore({
variables: { limit: 10 },
updateQuery: (prev, { fetchMoreResult }) => {
if (!fetchMoreResult) return prev;
return {
users: [...prev.users, ...fetchMoreResult.users]
};
}
})
}
>
Load More
</button>
</div>
);
}}
</Query>
);
}Render prop component for executing GraphQL mutations with optimistic updates.
/**
* Render prop component for GraphQL mutations
* @param props - Mutation component props with render function
* @returns JSX element from children render function
*/
function Mutation<TData = any, TVariables = OperationVariables>(
props: MutationComponentOptions<TData, TVariables>
): JSX.Element | null;
interface MutationComponentOptions<TData = any, TVariables = OperationVariables>
extends BaseMutationOptions<TData, TVariables> {
mutation: DocumentNode;
children: (
mutateFunction: MutationFunction<TData, TVariables>,
result: MutationResult<TData>
) => JSX.Element | null;
}Usage Examples:
import React from "react";
import { Mutation } from "react-apollo";
import gql from "graphql-tag";
const CREATE_USER = gql`
mutation CreateUser($input: UserInput!) {
createUser(input: $input) {
id
name
email
}
}
`;
function CreateUserForm() {
return (
<Mutation
mutation={CREATE_USER}
onCompleted={(data) => {
console.log('User created:', data.createUser);
}}
onError={(error) => {
console.error('Error creating user:', error);
}}
>
{(createUser, { data, loading, error }) => (
<form
onSubmit={async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const input = {
name: formData.get('name'),
email: formData.get('email')
};
try {
await createUser({
variables: { input },
optimisticResponse: {
createUser: {
__typename: 'User',
id: 'temp-id',
...input
}
},
update: (cache, { data }) => {
// Update cache with new user
const existingUsers = cache.readQuery({ query: GET_USERS });
cache.writeQuery({
query: GET_USERS,
data: {
users: [...existingUsers.users, data.createUser]
}
});
}
});
} catch (err) {
// Error handled by onError callback
}
}}
>
<input name="name" type="text" placeholder="Name" required />
<input name="email" type="email" placeholder="Email" required />
<button type="submit" disabled={loading}>
{loading ? 'Creating...' : 'Create User'}
</button>
{error && <div>Error: {error.message}</div>}
{data && <div>User created: {data.createUser.name}</div>}
</form>
)}
</Mutation>
);
}Render prop component for managing GraphQL subscriptions with real-time updates.
/**
* Render prop component for GraphQL subscriptions
* @param props - Subscription component props with render function
* @returns JSX element from children render function
*/
function Subscription<TData = any, TVariables = OperationVariables>(
props: SubscriptionComponentOptions<TData, TVariables>
): JSX.Element | null;
interface SubscriptionComponentOptions<TData = any, TVariables = OperationVariables>
extends BaseSubscriptionOptions<TData, TVariables> {
subscription: DocumentNode;
children?: null | ((result: SubscriptionResult<TData>) => JSX.Element | null);
}Usage Examples:
import React from "react";
import { Subscription } from "react-apollo";
import gql from "graphql-tag";
const MESSAGE_SUBSCRIPTION = gql`
subscription OnMessageAdded($channelId: ID!) {
messageAdded(channelId: $channelId) {
id
content
user {
name
}
createdAt
}
}
`;
function ChatSubscription({ channelId }) {
return (
<Subscription
subscription={MESSAGE_SUBSCRIPTION}
variables={{ channelId }}
onSubscriptionData={({ subscriptionData }) => {
console.log('New message received:', subscriptionData.data.messageAdded);
// Could trigger notifications, sound effects, etc.
}}
>
{({ data, loading, error }) => {
if (loading) return <div>Connecting to live chat...</div>;
if (error) return <div>Connection error: {error.message}</div>;
return (
<div>
{data && (
<div className="live-message">
<div className="message-header">
<strong>{data.messageAdded.user.name}</strong>
<span className="timestamp">
{new Date(data.messageAdded.createdAt).toLocaleTimeString()}
</span>
</div>
<p className="message-content">{data.messageAdded.content}</p>
</div>
)}
</div>
);
}}
</Subscription>
);
}
// Usage with multiple subscriptions
function MultiChannelChat({ channels }) {
return (
<div>
{channels.map((channel) => (
<div key={channel.id} className="channel">
<h3>{channel.name}</h3>
<ChatSubscription channelId={channel.id} />
</div>
))}
</div>
);
}import React from "react";
import { Query, Mutation } from "react-apollo";
const GET_TODOS = gql`
query GetTodos {
todos {
id
text
completed
}
}
`;
const TOGGLE_TODO = gql`
mutation ToggleTodo($id: ID!) {
toggleTodo(id: $id) {
id
completed
}
}
`;
function TodoApp() {
return (
<Query query={GET_TODOS}>
{({ data, loading, error }) => {
if (loading) return <div>Loading todos...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<Mutation mutation={TOGGLE_TODO}>
{(toggleTodo, { loading: mutationLoading }) => (
<ul>
{data.todos.map((todo) => (
<li key={todo.id}>
<label>
<input
type="checkbox"
checked={todo.completed}
disabled={mutationLoading}
onChange={() =>
toggleTodo({
variables: { id: todo.id },
optimisticResponse: {
toggleTodo: {
__typename: 'Todo',
id: todo.id,
completed: !todo.completed
}
}
})
}
/>
{todo.text}
</label>
</li>
))}
</ul>
)}
</Mutation>
);
}}
</Query>
);
}