RelayContainer

RelayContainer 是一个更高阶的 React 组件,它使 React 组件编码其数据需求。

  • Relay 确保在渲染组件之前该数据可用。
  • 只要基础数据发生变化,Relay 会更新组件。

使用 Relay.createContainer 创建 Relay 容器 。

概述 #

Container 规范

属性和方法

在一般的 React 组件中,容器将会提供一些方法和属性做为this.props.relay

静态方法

容器规范 #

片段 #

fragments: RelayQueryFragments<Tk> = {
  [propName: string]: (
    variables: {[name: string]: mixed}
  ) => Relay.QL`fragment on ...`
};

容器声明 fragments 使用GraphQL片段的数据要求。

this.props 当渲染组件时,只有这些片段指定的字段才会被填充。这确保了组件在其父组件或任何子组件上没有隐式的依赖关系。

示例 #

class StarWarsShip extends React.Component {
  render() {
    return <div>{this.props.ship.name}</div>;
  }
}

module.exports = Relay.createContainer(StarWarsShip, {
  fragments: {
    ship: () => Relay.QL`
      fragment on Ship {
        name
      }
    `,
  },
});

在此示例中,与 ship 片段相关联的字段将可用 this.props.ship

参见: 容器 > Relay 容器

初始变量 #

initialVariables: {[name: string]: mixed};

此组件片段可用的初始变量值集。

示例 #

class ProfilePicture extends React.Component {...}

module.exports = Relay.createContainer(ProfilePicture, {
  initialVariables: {size: 50},
  fragments: {
    user: () => Relay.QL`
      # The variable defined above is available here as '$size'.
      # Any variable referenced here is required to have been defined in initialVariables above.
      # An 'undefined' variable value will throw an 'Invariant Violation' exception.
      # Use 'null' to initialize unknown values.
      fragment on User { profilePicture(size: $size) { ... } }
    `,
  },
});

在这个例子中, profilePicture(size: 50) 将被提取为初始渲染。

prepareVariables #

prepareVariables: ?(
  prevVariables: {[name: string]: mixed}
) => {[name: string]: mixed}

容器可以定义一种 prepareVariables 提供修改可用于片段的变量的机会的方法。 除了运行时环境之外,还可以基于以前的变量(或者不存在先前的变量)生成新的变量。

在应用了部分变量集之后也会调用此方法 setVariables。返回的变量用于填充片段。

示例 #

module.exports = Relay.createContainer(ProfilePicture, {
  initialVariables: {size: 50},
  prepareVariables: prevVariables => {
    return {
      ...prevVariables,
      // If devicePixelRatio is `2`, the new size will be `100`.
      size: prevVariables.size * window.devicePixelRatio,
    };
  },
  // ...
});

shouldComponentUpdate #

shouldComponentUpdate: () => boolean;

RelayContainer 实现了一个传统的预设 shouldComponentUpdate ,在 fragment props 没有改变而且所有其他的 props 都是相等的值时会回传 false。这可能会阻挡由 context 接收数据的 组件的更新。为了确保在这个状况能进行更新,可以通过指定一个 shouldComponentUpdate 函数覆写预设的行为。

示例 #

module.exports = Relay.createContainer(ProfilePicture, {
  shouldComponentUpdate: () => true,
  // ...
});

属性和方法 #

从被打包的 React 组件可以访问在 this.props.relay 下面列出的属性和方法。

路由 #

route: RelayRoute

当一个组件在被渲染时,路由提供的上下文是非常有用的。它包含当下的路由有关 nameparams、和 queries 的信息。

示例 #

var name = this.props.relay.route.name;
if (name === 'SuperAwesomeRoute') {
  // Do something super cool.
}

可以参阅: 路由

变量 #

variables: {[name: string]: mixed}

variables 包含用于获取当前 props 的变量集。

示例 #

class ProfilePicture extends React.Component {
  render() {
    var user = this.props.user;
    return (
      <View>
        <Image
          uri={user.profilePicture.uri}
          width={this.props.relay.variables.size}
        />
      </View>
    );
  }
}
module.exports = Relay.createContainer(ProfilePicture, {
  initialVariables: {size: 50},
  fragments: {
    user: () => Relay.QL`
      fragment on User { profilePicture(size: $size) { ... } }
    `,
  },
});

在这个示例中,被渲染的图片 width 会对应到被用来获取目前 profilePicture.uri 版本所使用的 $size 变量。

附注

永远不要直接改动 this.props.relay.variables,它不会正确的触发获取数据。把 this.props.relay.variables 当作是不可变的来处理,就像 props 一样。

pendingVariables #

pendingVariables: ?{[name: string]: mixed}

pendingVariables 包含用于获取新 props 的一组变量,即何时 this.props.relay.setVariables()this.props.relay.forceFetch() 被调用, 并且相应的请求在进行中。

如果没有请求在进行中,pendingVariables 为null

示例 #

class ProfilePicture extends React.Component {
  requestRandomPictureSize = () => {
    const randIntMin = 10;
    const randIntMax = 200;
    const size = (Math.floor(Math.random() * (randIntMax - randIntMin + 1)) + randIntMin);
    this.props.relay.setVariables({size});
  }

  render() {
    const {relay, user} = this.props;
    const {pendingVariables} = relay;
    if (pendingVariables && 'size' in pendingVariables) {
      // Profile picture with new size is loading
      return (
        <View>
          <LoadingSpinner />
        </View>
      )
    }

    return (
      <View>
        <Image
          uri={user.profilePicture.uri}
          width={relay.variables.size}
        />
        <button onclick={this.requestRandomPictureSize}>
          Request random picture size
        </button>
      </View>
    );
  }
}
module.exports = Relay.createContainer(ProfilePicture, {
  initialVariables: {size: 50},
  fragments: {
    user: () => Relay.QL`
      fragment on User { profilePicture(size: $size) { ... } }
    `,
  },
});

在这个例子中,只要正在加载新尺寸的图片,就会显示一个调节框而不是图片。

setVariables #

setVariables([partialVariables: Object, [onReadyStateChange: Function]]): void

组件可以通过使用 setVariables 要求更新目前的 variables 组合,来改变它们的数据需求。

this.props.relay.setVariables 可以调用来同时更新子集或全部变量。作为返回,Relay 将使用新变量来尝试实现新的片段。如果客户端上的数据尚不可用,则可能会向服务器发送请求。

onReadyStateChange 可以提供可选的回调来响应数据执行所涉及的事件。

示例 #

class Feed extends React.Component {
  render() {
    return (
      <div>
        {this.props.viewer.feed.edges.map(
          edge => <Story story={edge.node} key={edge.node.id} />
        )}
      </div>
    );
  }
  _handleScrollLoad() {
    // Increments the number of stories being rendered by 10.
    this.props.relay.setVariables({
      count: this.props.relay.variables.count + 10
    });
  }
}
module.exports = Relay.createContainer(Feed, {
  initialVariables: {count: 10},
  fragments: {
    viewer: () => Relay.QL`
      fragment on Viewer {
        feed(first: $count) {
          edges {
            node {
              id,
              ${Story.getFragment('story')},
            },
          },
        },
      }
    `,
  },
});

Note

setVariables 不会立即变动 variables,但是会建立一个等待的状态。variables 会持续回传先前的值,直到满足新的变量值的数据填入了 this.props

请参阅: 容器 > 请求不同的数据, 准备状态

forceFetch #

forceFetch([partialVariables: Object, [onReadyStateChange: Function]]): void

forceFetch 类似于 setVariables,因为它也可以用来通过修改 variables 来改变数据需求。

两种方法的区别在于,不是发送仅包含客户端缺少的字段的查询,而是发送 forceFetch 请求以重新获取每个片段。这确保组件的 props 从服务器获取最新的。

onReadyStateChange e可以提供可选的回调来响应数据执行所涉及的事件。

附注

forceFetch 可以用一组空的部分变量来调用,这意味着它可以触发刷新当前渲染的数据集。

参见: 准备状态

hasOptimisticUpdate #

hasOptimisticUpdate(record: Object): boolean

用一个从 this.props 来的记录调用 hasOptimisticUpdate,会回传是否这个给定的记录有受到积极mutation 的影响。这让组件可以用不同的方式去渲染本地 积极变更和成功与服务器同步的数据。

示例 #

class Feed extends React.Component {
  render() {
    var edges = this.props.viewer.feed.edges;
    return (
      <div>
        {edges.map(edge => {
          var node = edge.node;
          if (this.props.relay.hasOptimisticUpdate(node)) {
            // Render pending story that has not been stored
            // on the server using a diffrent component.
            return (
              <PendingStory
                key={edge.node.id}
                story={edge.node}
              />
            );
          } else {
            return (
              <Story
                key={edge.node.id}
                story={edge.node}
              />
            );
          }
        })}
      </div>
    );
  }
}

module.exports = Relay.createContainer(Feed, {
  initialVariables: {count: 10},
  fragments: {
    viewer: () => Relay.QL`
      fragment on Viewer {
        feed(first: $count) {
          edges {
            node {
              id,
              ${Story.getFragment('story')},
              ${PendingStory.getFragment('story')}
            }
          }
        }
      }
    `,
  },
});

可以参阅: Mutations > 积极更新

getPendingTransactions #

getPendingTransactions(record: Object): ?Array<RelayMutationTransaction>

组件可以对任何的记录(例如:在 props 上可以取用的数据与一个对应的片段)检查是否有待处理的 mutation。使用记录调用 getPendingTransactions 将会回传一个影响特定 记录 的待处理的 mutation事务列表。

每个 RelayMutationTransaction 都有检查 mutation 状态的方法,并提供了根据需要回滚或重新发送突变的方法。.

示例 #

class Story extends React.Component {
  render() {
    var story = this.props.story;
    var transactions = this.props.relay.getPendingTransactions(story);
    // For this example, assume there is only one transaction.
    var transaction = transactions ? transactions[0] : null;
    if (transaction) {
      // Display an error message with a retry link if a mutation failed.
      if (transaction.getStatus() === 'COMMIT_FAILED') {
        return (
          <span>
            This story failed to post.
            <a onClick={transaction.recommit}>Try Again.</a>
          </span>
        );
      }
    }
    // Render story normally.
  }
}

module.exports = Relay.createContainer(ProfilePicture, {
  fragments: {
    story: () => Relay.QL`
      fragment on story {
        # ...
      }
    `,
  },
});

RelayMutationTransaction.getStatus 可以返回以下字符串之一:

  • UNCOMMITTED — 事务尚未发送到服务器,可以回滚。
  • COMMIT_QUEUED — 事务已提交,但具有相同冲突密钥的另一个事务处于挂起状态,因此事务已排队等待发送到服务器。
  • COLLISION_COMMIT_FAILED — 事务已排队等待提交,但具有相同冲突键的另一个事务失败。碰撞队列中的所有事务(包括此事件)都已失败。可以重新提交或回滚事务。
  • COMMITTING — 事务正在等待服务器的响应。
  • COMMIT_FAILED — 事务已发送到服务器进行配对,但是失败了。

静态方法 #

getFragment #

getFragment(
  fragmentName: string,
  variables?: {[name: string]: mixed}
): RelayFragmentReference

获取对子容器的片段的引用,以便包含在父片段中。

示例 #

片段组成通过ES6模板串插值实现 getFragment:

// Parent.js
Relay.createContainer(Parent, {
  fragments: {
    parentFragment: () => Relay.QL`
      fragment on Foo {
        id
        ${Child.getFragment('childFragment')}
      }
    `,
  }
});
// Child.js
Relay.createContainer(Child, {
  initialVariables: {
    size: 64,
  },
  fragments: {
    childFragment: () => Relay.QL`
      fragment on Foo {
        photo(size: $size) { uri }
      }
    `,
  }
});

在这个例子中,每当 Parent 被获取时, Child片段也将被获取。渲染时,<Parent> 只能访问该 props.foo.id 字段; 来自子片段的数据将被 屏蔽。默认情况下, childFragment 将使用其对应的初始变量。Relay 将提取 photo(size: 64)。 当 <Child>被渲染它也将提供 props.relay.variables = {size: 64}作为初始变量。

覆写片段变量 #

有时父进程需要覆写子组件的默认变量。想象一下,我们想要渲染 Child 上面的照片大小为128而不是默认值64。 为此,我们必须确保片段 容器都知道自定义变量。要在 查询 中设置自定义变量,请使用第二个参数 getFragment:

// Parent.js
Relay.createContainer(Parent, {
  fragments: {
    parentFragment: () => Relay.QL`
      fragment on Foo {
        id
        ${Child.getFragment('childFragment', {size: 128})}
      }
    `,
  }
});

现在 Relay 将获取大小为 128 的照片 - 但 Child 容器不会神奇地知道这个变量。我们必须通过将变量值传递给一个 prop 来告诉它:

const Parent = (props) => {
  return (
    <Child
      childFragment={props.parentFragment}
      size={128}
    />;
  );
}

现在 Relay 将获取较大的照片大小 Child 并将知道渲染它。