如何将变量传递给 GraphQL 查询?

How to pass variables to a GraphQL query?

假设我在 React-Apollo 中有以下 GraphQL 查询

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

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

现在如果我想让我的 React 组件 Profile 更改 avatarID,

我该怎么做?

我是 React 和 GraphQl 的新手,我不太了解这里的联系:

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

我真的需要另一个父组件吗 ProfileWithData 将另一个 avatarID 传递给查询?但是,如果 ID 被 Profile 组件操纵,我该如何让 Parent 组件知道呢?

您的 ProfileWithData 组件可以很好地渲染特定用户的头像。本组件不负责管理当前显示的头像组件的变化。

一种方法是添加一个从父组件向下传递的新道具 avatarId。那么你需要这样写:

graphql(CurrentUserForLayout, {
  options: (ownProps) => ({
    variables: {
      id: ownProps.avatarId
    }
  })
})(Profile);

如果您熟悉react-router,另一种方法是使用路由参数来确定头像id。您需要像这样调整 graphql 的调用:

graphql(CurrentUserForLayout, {
  options: (ownProps) => ({
    variables: {
      id: ownProps.params.avatarId
    }
  })
})(Profile);

要详细了解此类查询变量的用法,您可以查看 React Track of Learn Apollo

到目前为止,我知道两种可能的方法。

第一个已经在问题中说明了。变量同时是ProfileWithData的道具。所以你需要找到一种方法来改变道具并重新渲染组件。这通常是通过父组件来实现的。所以通常你不会改变ProfileWithData里面的变量。该组件应仅用于显示目的。但是,这并不是说不能做到。您可以将父组件的方法传递给子组件以操纵父组件的状态。这将重新渲染两个组件并使用新的 props/variables.

渲染 ProfileWithData

第二种方法是从ProfileWithData组件内部查询。然后,您可以使用状态从组件内部操作变量。可以找到执行此操作的方法

You can do it by passing a reference to Apollo Client using the withApollo higher-order-component, as documented here: http://dev.apollodata.com/react/higher-order-components.html#withApollo

Then, you can call client.query on the passed in object, like so:

 class MyComponent extends React.Component {
      runQuery() {
        this.props.client.query({
          query: gql`...`,
          variables: { ... },
        });
      }

      render() { ... }
    }

    withApollo(MyComponent);

Apollo 客户端 2.1 之前

在 Apollo Client 2.1 之前,当您必须使用 Higher-Order Components(HOC)时,henk 在另一个答案中正确指出了两个解决方案。我试着简单总结一下:

  • 由于HOC只能访问props,所以需要在父组件中引入changing变量。例如,父组件可以使用 React 的 setState() 方法对不断变化的变量进行本地状态管理。一旦本地状态中的变量发生变化,它就可以传递给您的其他组件,该组件由 HOC 增强。 HOC 可以访问 props 并可以使用更改后的变量执行查询。 提示:如果您不想在两者之间引入另一个组件来更改本地状态,recomposewithState HOC 可能是一个不错的选择。

  • 第二种方式,但可能不太优雅,因为它将您的代码从声明式转变为命令式,将使用 withApollo() HOC,它使您可以访问 Apollo Client 实例作为支柱。在您的 React 组件中拥有此实例,一旦您的变量发生变化,您就可以在组件的 class 方法之一(例如 onChange 处理程序)中调用 client.query({ query: ..., variables: ... })

自 Apollo 客户端 2.1

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 以来。但是,您的组件中使用了新的 Query(和 Mutation)组件。它使用 React 的渲染道具模式。

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

通过将此控制流转移到组件内部,而不是让它仅与 HOC 位于同一位置,您可以将不断变化的 prop 作为变量传递给您的 Query(和 Mutation)组件。

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

如果你想学习 React 中的 Apollo Client,请查看 this extensive tutorial。注意:代码片段取自 Apollo Client 2.1 发布博客 post.