1
|
import { graphql } from 'react-apollo';
|
graphql()
函数是
react-apollo
中最重要的部分。使用此函数,你可以创建基于 Apollo store 中的数据来反应性地执行查询和更新的高阶组件。
graphql()
函数返回一个函数,它将“增强”任何具有反应性 GraphQL 功能的组件。这与
react-redux
的
connect
函数使用的 React
高阶组件
模式一致。
graphql()
函数可以这样使用:
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);
|
你还可以定义一个中间函数,使用
graphql()
函数将组件联接起来,如下所示:
1 2 3 4 5 6 7 8
|
const withTodoAppQuery = graphql(gql`query { ... }`); const TodoAppWithData = withTodoAppQuery(TodoApp); export default TodoAppWithData;
|
或者,你还可以在 React 类组件上使用
graphql()
函数作为
装饰器
。
如果是这样,你的代码可能如下所示:
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> ); } }
|
如果你的组件树根部有一个
<ApolloProvider/>
组件提供一个
ApolloClient
实例用于获取数据,
graphql()
函数将只能提供对 GraphQL 数据的访问。
根据 GraphQL 的操作是一个
查询
,还是一个
突变
,或是一个
订阅
,你使用
graphql()
函数增强的组件的行为会有所不同。有关每种类型的功能和可用选项的更多信息,请参阅相应的API文档。
在我们研究每种操作的具体行为之前,让我们先看看
config
对象。
config
在你的 GraphQL 文本之后,
config
对象是你传入
graphql()
函数的第二个参数。该配置是可选的,并允许你向高阶组件添加一些自定义行为。
1 2 3 4
|
export default graphql( gql`{ ... }`, config, )(MyComponent);
|
让我们来看看你的
config
对象所有可能的属性。
config.options
config.options
是一个对象或函数,它允许你定义组件在处理 GraphQL 数据时应使用的具体行为。
可用于配置的具体选项取决于你为
graphql()
传递的第一个参数 – 操作类型。针对
查询
和
突变
,有特定的选项可以配置。
你可以将
config.options
定义为普通对象,或者你可以从一个函数中计算你的选项,该函数接收组件的 props 作为参数。
1 2 3 4 5
|
export default graphql(gql`{ ... }`, { options: { }, })(MyComponent);
|
1 2 3 4 5
|
export default graphql(gql`{ ... }`, { options: (props) => ({ }), })(MyComponent);
|
config.props
config.props
属性允许你定义一个 map 函数,它包含你的所有 props,包括由
graphql()
函数(用于查询的
props.data
,用于突变的
props.mutate
)添加的属性,并且允许你计算一个新的 props 对象,提供给
graphql()
正在包装的组件。
你定义的函数几乎与
Recompose 中的
mapProps
完全一样,提供了相同的便利,并且不需要引入一个新的库。
如果你想将复杂的函数调用抽象成一个简单的,可以传递给你的组件的 prop,那么
config.props
是最有用的。
config.props
的另一个好处,是它也允许你将你的纯 UI 组件与 GraphQL 和 Apollo 的关系分离开来。你可以将纯 UI 组件写在一个文件里,然后保留所需的逻辑,以便它们在项目中别的地方与 store 进行交互。你可以通过只需要渲染所需的 props 的纯 UI 组件实现这个,
config.props
可以包含从 GraphQL API 提供的数据中完全提供你的纯组件所需的 props 的逻辑。
此示例使用了
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
如果
config.skip
值为真,那么所有的 React Apollo 代码将被
完全
跳过。就好像
graphql()
函数是一个简单的标识函数。你的组件将会像
graphql()
函数不存在那般。
除了将布尔值传递给
config.skip
,你也可以将函数传递给
config.skip
。该函数将接收你的组件 props,并返回一个布尔值。如果布尔值返回真,则跳过行为将生效。
如果你想要基于某些 prop 使用不同的查询,
config.skip
将会十分有用。你可以在下面的示例中看到这一点。
1 2 3
|
export default graphql(gql`{ ... }`, { skip: props => !!props.skip, })(MyComponent);
|
以下示例使用
compose()
函数同时使用多个
graphql()
增强器。
1 2 3 4 5 6 7 8 9
|
export default compose( graphql(gql`query MyQuery1 { ... }`, { skip: props => !props.useQuery1 }), graphql(gql`query MyQuery2 { ... }`, { skip: props => props.useQuery1 }), )(MyComponent); function MyComponent({ data }) { console.log(data); }
|
config.name
该属性允许你配置传递给组件的 prop 的名称。默认情况下,如果你传递给
graphql()
的 GraphQL 文本是一个查询,那么你的 prop 将被命名为
data
。如果你传递的是一个突变,那么你的 prop 将被命名为
mutate
。当你尝试为同一个组件的使用多个查询或突变时,这些默认名称会相互冲突。为了避免冲突,你可以使用
config.name
为每个查询或突变高阶组件提供一个新的名字给 prop。
此示例使用
compose
函数将多个
graphql()
高阶组件组合在一起。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
export default compose( graphql(gql`mutation (...) { ... }`, { name: 'createTodo' }), graphql(gql`mutation (...) { ... }`, { name: 'updateTodo' }), graphql(gql`mutation (...) { ... }`, { name: 'deleteTodo' }), )(MyComponent); function MyComponent(props) { console.log(props.createTodo); console.log(props.updateTodo); console.log(props.deleteTodo);
|
config.withRef
通过将
config.withRef
设置为真,你可以使用高阶 GraphQL 组件实例上的
getWrappedInstance
方法从该高阶 GraphQL 组件实例获取包裹组件。
当你想要调用在包裹组件的类实例上定义的函数或访问其属性时,可能需要将其设置为真。
下面是一个这种情况下的例子。
此示例使用
React
ref
功能
。
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); // 我们可以在组件类实例上调用方法。 component.saySomething(); }} /> ); } }
|
config.alias
默认情况下,React Apollo 组件的显示名称为
Apollo(${WrappedComponent.displayName})
。这是大多数使用高阶组件的 React 库所使用的模式。但是,当你使用多个高阶组件时查看 [React Devtools][],可能会有点困惑。
如果要配置高阶组件包装器的名称,可以使用
config.alias
属性。因此,假设你将
config.alias
设置为
'withCurrentUser'
,你的包装器组件显示名称将是
withCurrentUser(${WrappedComponent.displayName})
,而不是
Apollo(${WrappedComponent.displayName})
。
此示例使用
compose
函数将多个
graphql()
高阶组件组合在一起。
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';
|
为了实用目的,
react-apollo
导出一个
compose
函数。使用此函数,你可以利落地一次使用多个组件增强器。包括多个
graphql()
,
withApollo()
或
Redux
connect()
增强器。当你使用多个增强器时,这将会理清你的代码。
Redux
也导出一个
compose
函数,
Recompose
也是如此,所以你可以有选择地从合适的库中使用这个函数。
一个重要的注意事项是,
compose()
首先
执行最后一个增强器,并根据增强器列表依次向后执行。为了说明这种情况,我们调用三个函数:
funcC(funcB(funcA(component)))
相当于这样调用
compose()
:
compose(funcC, funcB, funcA)(component)
。如果还不明白,你可以想象 [Lodash 中的
flowRight()
][] 函数,它们具有相同的行为。
1 2 3 4 5 6
|
export default compose( withApollo, graphql(`query { ... }`), graphql(`mutation { ... }`), connect(...), )(MyComponent);
|