FragmentContainer

声明数据需求的主要途径是通过 createFragmentContainer 一个更高阶的 React 组件,让 React 组件对其数据需求进行编码。

类似于React组件的 render 方法不直接修改原生视图,不会直接获取数据。 相反,容器声明了渲染所需的数据的 规范 保证在渲染. Relay 保证 渲染 之前 数据是可用的。

一个完整的例子 #

首先,我们来构建一个 <TodoItem> 显示文本和完成状态的组件的简单的 React 版本Todo

基本 React 组件 #

这是一个基本 <TodoItem> 的实现,,忽略了样式,以 mutation 显示功能:

class TodoItem extends React.Component {
  render() {
    // Expects the `item` prop to have the following shape:
    // {
    //   item: {
    //     text,
    //     isComplete
    //   }
    // }
    const item = this.props.item;
    return (
      <View>
        <Checkbox checked={item.isComplete} />
        <Text>{item.text}</Text>
      </View>
    );
  }
}

使用GraphQL解决数据依赖关系 #

在 Relay 中, 使用 GraphQL 描述数据依赖关系。对于 <TodoItem>, 依赖性可以表示如下。注意,这完全符合组件预期的 item prop 的格式。

<FileName>_<propName> 建议使用片段的命名约定。在从传统API到现代API的迁移过程中需要进行这种限制,以允许交叉兼容。

graphql`
  # This fragment only applies to objects of type 'Todo'.
  fragment TodoItem_item on Todo {
    text
    isComplete
  }
`

Relay 容器 #

给定了一个简单的 React 组件和一个 GraphQL 片段,现在我们可以定义一个 Container 告诉Relay关于这个组件的数据需求。让我们先看看代码,然后看看发生了什么:

class TodoItem extends React.Component {/* as above */}

// Export a *new* React component that wraps the original `<TodoItem>`.
module.exports = createFragmentContainer(TodoItem, {
  // For each of the props that depend on server data, we define a corresponding
  // key in this object. Here, the component expects server data to populate the
  // `item` prop, so we'll specify the fragment from above at the `item` key.
  item: graphql`
    fragment TodoItem_item on Todo {
      text
      isComplete
    }
  `,
});

上面的例子与经典容器API非常相似,但是在现代API中,我们可以直接将graphql 模板文字作为第二个参数传递。 Relay 将根据片段命名约定从片段名称推断该名称 <FileName>_<propName>. 下面的例子等同于上面的例子:

module.exports = createFragmentContainer(
  TodoItem,
  graphql`
    fragment TodoItem_item on Todo {
      text
      isComplete
    }
  `,
);

如果没有 _<propName> 后续, data 将使用prop名称:

class TodoItem extends React.Component {
  render() {
    const item = this.props.data;

  }
}
module.exports = createFragmentContainer(
  TodoItem,
  graphql`
    fragment TodoItem on Todo {
      text
      isComplete
    }
  `,
);

容器组合 #

React 和 Relay 支持通过 组合. 创建任意复杂的应用程序。可以通过组合较小的组件来创建更大的组件,帮助我们创建模块化,健壮的应用程序。在 Relay 中组成组件有两个方面:

  • 构成视图逻辑, 和
  • 组合数据描述。

我们来探讨如何通过从上面的 <TodoList> 组成组件 <TodoItem>。/p>

组合视图 - 这是 Plain React #

视图组合 正是 你以往所做的 — Relay 容器是标准的 React 组件。 这是 <TodoList> 组件:

class TodoList extends React.Component {
  render() {
    // Expects a `list` with a string `title`, as well as the information
    // for the `<TodoItem>`s (we'll get that next).
    const list = this.props.list;
    return (
      <View>
        {/* It works just like a React component, because it is one! */}
        <Text>{list.title}</Text>
        {list.todoItems.map(item => <TodoItem item={item} />)}
      </View>
    );
  }
}

片段组合 #

片段组合的作用类似 - 父容器的片段为每个子节点组成的片段。在这种情况下, <TodoList> 需要获取<TodoItem>需要的 Todos 信息 。

class TodoList extends React.Component {/* as above */}

module.exports = createFragmentContainer(
  TodoList,
  // This `_list` fragment name suffix corresponds to the prop named `list` that
  // is expected to be populated with server data by the `<TodoList>` component.
  graphql`
    fragment TodoList_list on TodoList {
      # Specify any fields required by '<TodoList>' itself.
      title
      # Include a reference to the fragment from the child component.
      todoItems {
        ...TodoItem_item
      }
    }
  `,
);

注意,当编写片段时,组合片段的类型必须与嵌入其中的父对象的字段相匹配。 例如,嵌入一个 Story 类的片段到父 User 类的字段中没有意义。如果你错了,Relay 和 GraphQL 将会提供有用的错误信息 (如果没有帮助,请让我们知道!)。

调用组件实例方法 #

React 组件可能有方法, 通常通过 引用访问。 因为 Relay 在容器中组成这些组件实例,因此您需要使用 componentRef 支持来访问它们:

考虑使用服务器定义的占位符文本和用于聚焦输入节点的命令式方法的:

module.exports = createFragmentContainer(
  class TodoInput extends React.Component {
    focus() {
      this.input.focus();
    }

    render() {
      return <input
        ref={node => { this.input = node; }}
        placeholder={this.props.data.suggestedNextTitle}
      />;
    }
  },
  graphql`
    fragment TodoInput on TodoList {
      suggestedNextTitle
    }
  `,
);

要在底层组件上调用此方法,首先向 componentRef Relay 容器提供一个函数。这不同于提供一个 ref 功能, 该功能将提供对 Relay 容器本身的引用,而不是底层的 React 组件。

module.exports = createFragmentContainer(
  class TodoListView extends React.Component {
    render() {
      return <div onClick={() => this.input.focus()}>
        <TodoInput
          data={this.props.data}
          componentRef={ref => { this.input = ref; }}
        />
      </div>;
    }
  },
  graphql`
    fragment TodoListView on TodoList {
      ...TodoInput
    }
  `,
);

渲染容器 #

据我们所知,Relay 片段容器将数据要求声明为GraphQL片段。我们几乎可以让 Relay 满足这些组件的数据要求并进行渲染。不过,有一个问题。为了使用 GraphQL 实际地抓取数据,我们需要查询根。举例来说,我们需要把 <TodoList> 片段放在一个 GraphQL 查询中。

在 Relay 中,查询的根通过 QueryRenderer 来定义,请查看相关章节以了解更多细节。