Apollo 2.1多查询/突变组件使用求助:依赖查询与多突变问题
Hey there! Let's break down your Apollo 2.1 questions step by step—these are really common scenarios when building with GraphQL and React, so I’ve got you covered.
In Apollo 2.1, the cleanest approach for dependent queries is to nest Query components. Since each Query waits for its data to resolve before rendering its children, you can safely pass the first query's result as variables to the second.
Here's a concrete example: let's say you first fetch a user's ID, then use that ID to pull their associated orders.
import { Query } from 'react-apollo'; import gql from 'graphql-tag'; // First query: Fetch current user's ID const GET_CURRENT_USER = gql` query GetCurrentUser { currentUser { id } } `; // Second query: Fetch orders using a user ID variable const GET_USER_ORDERS = gql` query GetUserOrders($userId: ID!) { userOrders(userId: $userId) { id total date } } `; function UserOrders() { return ( <Query query={GET_CURRENT_USER}> {({ loading: userLoading, error: userError, data: userData }) => { if (userLoading) return <p>Loading user data...</p>; if (userError) return <p>Oops, failed to load user: {userError.message}</p>; // Once we have the user ID, pass it to the nested Query const userId = userData.currentUser.id; return ( <Query query={GET_USER_ORDERS} variables={{ userId }}> {({ loading: ordersLoading, error: ordersError, data: ordersData }) => { if (ordersLoading) return <p>Loading your orders...</p>; if (ordersError) return <p>Failed to load orders: {ordersError.message}</p>; return ( <div> <h3>Your Recent Orders</h3> <ul> {ordersData.userOrders.map(order => ( <li key={order.id}> Order #{order.id} - ${order.total} ({order.date}) </li> ))} </ul> </div> ); }} </Query> ); }} </Query> ); }
Key notes:
- Handle loading/error states for both queries separately to keep the UI responsive and user-friendly.
- The inner Query only renders once the outer query has successfully returned data—so you’ll never pass an undefined
userIdas a variable. - For longer dependency chains, you can keep nesting Query components as needed. If you ever upgrade beyond Apollo 2.1,
useQueryhooks offer more concise ways to handle this, but nested components are the go-to for your current version.
Adding multiple Mutation components to a Query-based component is totally straightforward—you can nest them inside the Query's render prop or place them alongside each other. Each Mutation exposes its own mutate function that you can trigger with user actions (like button clicks).
Let's extend the previous example to add two mutations: one to update an order's status, and another to delete an order.
import { Query, Mutation } from 'react-apollo'; import gql from 'graphql-tag'; // Reuse the GET_CURRENT_USER and GET_USER_ORDERS queries from above // Mutation 1: Update an order's status const UPDATE_ORDER_STATUS = gql` mutation UpdateOrderStatus($orderId: ID!, $status: String!) { updateOrderStatus(orderId: $orderId, status: $status) { id status } } `; // Mutation 2: Delete an order const DELETE_ORDER = gql` mutation DeleteOrder($orderId: ID!) { deleteOrder(orderId: $orderId) { id } } `; function UserOrders() { return ( <Query query={GET_CURRENT_USER}> {({ loading: userLoading, error: userError, data: userData }) => { if (userLoading) return <p>Loading user data...</p>; if (userError) return <p>Oops, failed to load user: {userError.message}</p>; const userId = userData.currentUser.id; return ( <Query query={GET_USER_ORDERS} variables={{ userId }}> {({ loading: ordersLoading, error: ordersError, data: ordersData, refetch }) => { if (ordersLoading) return <p>Loading your orders...</p>; if (ordersError) return <p>Failed to load orders: {ordersError.message}</p>; // Use `refetch` to refresh the order list after mutations return ( <div> <h3>Your Recent Orders</h3> <ul> {ordersData.userOrders.map(order => ( <li key={order.id}> <div> Order #{order.id} - ${order.total} ({order.date}) <span>Status: {order.status}</span> </div> {/* Mutation 1: Mark order as shipped */} <Mutation mutation={UPDATE_ORDER_STATUS} onCompleted={() => refetch()} // Refresh orders after success > {(updateStatus, { error: updateError }) => ( <> <button onClick={() => updateStatus({ variables: { orderId: order.id, status: 'Shipped' } })}> Mark as Shipped </button> {updateError && <p className="error">Failed to update status: {updateError.message}</p>} </> )} </Mutation> {/* Mutation 2: Delete the order */} <Mutation mutation={DELETE_ORDER} onCompleted={() => refetch()} // Refresh orders after deletion > {(deleteOrder, { error: deleteError }) => ( <> <button onClick={() => { if (window.confirm('Are you sure you want to delete this order?')) { deleteOrder({ variables: { orderId: order.id } }); } }}> Delete Order </button> {deleteError && <p className="error">Failed to delete order: {deleteError.message}</p>} </> )} </Mutation> </li> ))} </ul> </div> ); }} </Query> ); }} </Query> ); }
Key points:
- Each Mutation component is independent—you can add as many as you need in the same component.
- Use the
onCompletedprop to callrefetch()on the Query after a mutation succeeds. This ensures the order list updates to reflect the new state. - The Mutation's render prop gives you access to the
mutatefunction (we named themupdateStatusanddeleteOrderfor clarity) which you can tie to user interactions like button clicks. - Don’t forget to handle mutation errors by destructuring the
errorobject from the Mutation's render prop.
内容的提问来源于stack exchange,提问作者brunodahora

