Relay Modern RefetchContainer道具未传递给组件

时间:2017-07-21 19:48:19

标签: javascript react-redux graphql relayjs relaymodern

我在Relay Modern中设置refetchContainer时遇到了一些问题。父组件是QueryRenderer,它运行初始查询,适当地填充子组件的道具(a-prop-riately?eh?eh?!)。 refetchContainer指定我们所有的变量,并在输入字段的onChange事件上,使用新变量重新运行查询。这一切都很完美,除了孩子的道具永远不会更新收到的新数据。我可以向下钻取Relay存储,看到确实收到了带有适当数据的查询。是bangin'我的头反对这一段时间我会感激一些帮助。我可能缺少一些简单的东西。 Lord知道Relay Modern文档很少。

我已经四处寻找,无法找到合适的解决方案。这家伙似乎有类似的问题: relay refetch doesn't show the result

使用QueryRenderer的父组件:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { graphql, QueryRenderer } from 'react-relay';
import Search from './Search';

const propTypes = {
  auth: PropTypes.object.isRequired,
};

class SearchContainer extends Component {
  render() {
    return (
      <QueryRenderer
        query={graphql`
          query SearchContainerQuery($search: String!){
            users: searchUsers(search:$search, first:10){
              ...Search_users
            }
          }`}
        variables={{ search: 'someDefaultSearch' }}
        environment={this.props.auth.environment}
        render={({ error, props }) => {
          if (error) {
            console.log(error);
          }
          if (props) {
            return <Search users={props.users} />;
          }
          return <div>No Dice</div>;
        }}
      />
    );
  }
}

SearchContainer.propTypes = propTypes;

export default connect(state => ({ auth: state.auth }))(SearchContainer);

带有createRefetchContainer的子组件:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { createRefetchContainer, graphql } from 'react-relay';

const propTypes = {
  relay: PropTypes.object.isRequired,
  users: PropTypes.object,
};

const defaultProps = {
  users: {},
};

class Search extends Component {
  render() {
    return (
      <div>
        <input
          type="text"
          onChange={(e) => {
            e.preventDefault();
            this.props.relay.refetch({
              search: e.target.value,
            });
          }}
        />
        <ul>
          {this.props.users.nodes.map(user =>
            <li key={user.id}>{user.username}</li>,
          )}
        </ul>
      </div>
    );
  }
}

Search.propTypes = propTypes;
Search.defaultProps = defaultProps;

export default createRefetchContainer(
  Search,
  {
    users: graphql.experimental`
      fragment Search_users on SearchUsersConnection
      @argumentDefinitions(
        search: {type: "String", defaultValue: ""}
      ) {
        nodes {
            id
            displayName
            username
          }
      }
    `,
  },
  graphql.experimental`
    query SearchRefetchQuery($search: String!) {
      users: searchUsers(search:$search, first:10){
        ...Search_users @arguments(search: $search)
      }
    }
  `,
);

GraphQL看起来像这样:

# A connection to a list of `User` values.
type SearchUsersConnection {
  # Information to aid in pagination.
  pageInfo: PageInfo!

  # The count of *all* `User` you could get from the connection.
  totalCount: Int

  # A list of edges which contains the `User` and cursor to aid in pagination.
  edges: [SearchUsersEdge]

  # A list of `User` objects.
  nodes: [User]
}

正确进行网络调用,并按预期返回数据。 NetworkCalls

似乎@arguments指令可以在这里删除重新获取查询:

query SearchRefetchQuery($search: String!) {
      users: searchUsers(search:$search, first:10){
          ...Search_users @arguments(search: $search)
      }
}

(删除它似乎没有效果)

我已经尝试将@arguments指令添加到父组件的片段中,按照此处的建议:Pass variables to fragment container in relay modern,无效。

2 个答案:

答案 0 :(得分:4)

哦,我想我遇到了类似的问题。如果我没记错的话,我相信“id”Relay用来识别要传递道具的组件,包括初始查询的变量,所以尝试从QueryRenderer的查询中解开容器片段。

对我有用的设置如下:

Logs.setText

refetchContainer看起来像:

<QueryRenderer
  variables={{search: 'someDefaultSearch', count: 10}}
  query={graphql`
    query SearchContainerQuery($search: String!, $count: Int!){
      # if we want defaults we need to "prepare" them from here
      ...Search_users @arguments(search: $search, count: $count)
    }
  `}
  environment={this.props.auth.environment}
  render={({ error, props: relayProps }) => {
    // I use relayProps to avoid possible naming conflicts
    if (error) {
      console.log(error);
    }
    if (relayProps) {
      // the users props is "masked" from the root that's why we pass it as is
      return <Search users={relayProps} />;
    }
    return <div>No Dice</div>;
  }}
/>

请注意,上面的示例假设Relay v1.3不推荐使用export default createRefetchContainer( Search, { users: graphql` # In my schema the root is called RootQueryType fragment Search_users on RootQueryType @argumentDefinitions( search: {type: "String"} count: {type: "Int!"} ) { searchUsers(search: $search, first: $count) { nodes { id displayName username } } } ` }, graphql` query SearchRefetchQuery($search: String!, $count: Int!) { ...Search_users @arguments(search: $search, count: $count) } ` ); 。此外,我不记得使用graphql.experimental技巧是否可以让您对@arguments工作进行别名处理。

我的最后一个建议是打开调试器,看看在获取数据时会发生什么。

最后,根据您的意见,我同意这可能是一个BUG。让我们看看你报告的问题中的事情是如何演变的。

答案 1 :(得分:4)

正如其他答案所述,这实际上是预期的行为,我会尝试稍微扩展答案。

当调用refetch并执行refetchQuery时,Relay实际上并未使用查询结果重新呈现组件。它所做的只是将有效负载规范化到商店并触发任何相关的订阅。这意味着如果获取的数据与已安装容器订阅的数据无关(例如,使用完全不同的节点ID,并且没有任何数据重叠),则组件不会重新呈现

在这个特定场景中,容器没有重新呈现的原因,即为什么它没有订阅refetchQuery触发的更改,是由于如何设置订阅起来。订阅基本上是对snapshot的订阅,它基本上代表一个具体的片段以及在给定时间点与之关联的数据和记录 - 当与快照相关的记录发生变化时,该快照的订阅将通知变更。

在这种情况下,假设容器的片段没有变量,它将订阅的快照将只与初始节点相关联;在重新发生并且存储被更新之后,新节点的记录将被更新,但与原始快照相关联的记录没有改变,因此订阅它的容器不会被通知。

这意味着当您更改组件片段中的变量时,确实只能使用Refetch容器。这里提供的解决方案是有效的,即在片段中包含重新获取容器的变量或在QueryRenderer中将新变量设置为一级。

这绝对可以在文档中更清晰,因此我会更新它们以反映这一点。我希望这有帮助!