13

我在 Relay Modern 中设置 refetchContainer 时遇到了一些问题。父组件是 QueryRenderer,它运行初始查询,适当地填充子组件的 props (a-prop-riately?eh?eh?!)。refetchContainer 指定了我们所有的变量,并且在输入字段的 onChange 事件中,使用新变量重新运行查询。这一切都很完美,除了孩子的道具永远不会随着收到的新数据而更新。我可以深入了解 Relay 存储,并查看确实收到了带有适当数据的查询。一段时间以来一直在努力解决这个问题,我将不胜感激。可能我缺少一些简单的东西。上帝知道 Relay Modern 的文档很少。

我四处寻找,找不到合适的解决方案。这家伙似乎遇到了类似的问题: relay refetch does not 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]
}

适当地进行网络调用,并按预期返回数据。 网络通话

似乎 @arguments 指令可以被排除在 refetch 查询之外:

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

(删除它似乎没有效果)

我已经尝试按照此处的建议将@arguments 指令添加到父组件的片段中:将变量传递到中继现代中的片段容器,但没有效果。

4

2 回答 2

6

正如其他答案中提到的,这实际上是预期的行为,我将尝试稍微扩展答案。

当调用 refetch 并refetchQuery执行时,Relay 实际上并没有使用查询的结果来重新渲染组件。它所做的只是将有效负载标准化到存储中并触发任何相关订阅。这意味着如果获取的数据与已挂载的容器订阅的数据无关(例如,使用完全不同的节点 id,没有任何数据重叠),那么组件将不会重新渲染。

在这种特定情况下,容器不重新渲染的原因,即它不订阅由 触发的更改的refetchQuery原因是由于订阅的设置方式。订阅基本上是对 a 的订阅snapshot,它基本上代表一个具体的片段以及在给定时间点与之关联的数据和记录——当与快照关联的记录发生更改时,将通知该快照的订阅更改.

在这种情况下,鉴于容器的片段没有变量,它将订阅的快照将仅与初始节点相关联;在 refetch 发生并更新 store 后,新节点的记录将被更新,但与原始快照关联的记录没有更改,因此订阅它的容器不会收到通知。

这意味着 Refetch 容器仅在您更改组件片段中的变量时才真正使用。此处提供的解决方案是有效的,即在片段中包含用于重新获取容器的变量或在 QueryRenderer 中设置新变量上一级。

这肯定可以在文档中更清楚地说明,所以我会更新它们以反映这一点。我希望这有帮助!

于 2018-01-03T16:25:56.383 回答
5

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

对我有用的设置如下所示:

<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>;
  }}
/>

refetchContainer 看起来像:

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

请注意,上面的示例假定 Relay v1.3graphql.experimental已弃用。另外,我不记得是否可以使用该@arguments技巧使您的searchUsers工作混叠方法。

我的最后一个建议是打开你的调试器,看看当你得到数据时发生了什么。

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

于 2017-12-22T20:25:36.620 回答