Apollo将第一个结果复制到边数组中的每个节点

时间:2018-02-17 10:14:35

标签: apollo react-apollo

我正在使用react-apollo开发一个react应用程序 当我检查浏览器网络选项卡响应时,通过graphql调用数据它显示数组的所有元素不同 但是我在我的应用程序中获得或console.log()然后所有数组元素与第一个元素相同。 我不知道如何修复请帮助

3 个答案:

答案 0 :(得分:5)

发生这种情况的原因是因为数组中的项目得到了标准化" Apollo缓存中的相同值。 AKA,他们看起来和阿波罗一样。这通常是因为它们共享相同的Symbol(id)

如果您打印出您的Apollo响应对象,您会注意到每个对象都有Symbol(id) Apollo缓存使用的对象。您的数组项可能具有相同的Symbol(id),这会导致它们重复。为什么会这样?

默认情况下,Apollo缓存会运行此function进行规范化。

export function defaultDataIdFromObject(result: any): string | null {
  if (result.__typename) {
    if (result.id !== undefined) {
      return `${result.__typename}:${result.id}`;
    }
    if (result._id !== undefined) {
      return `${result.__typename}:${result._id}`;
    }
  }
  return null;
}

您的数组项属性会导致多个项返回相同的数据ID。在我的情况下,多个项目_id = null导致所有这些项目重复。当此函数返回null时,docs

  

InMemoryCache将回退到查询中对象的路径,   例如ROOT_QUERY.allPeople.0为返回的第一条记录   allPeople根查询。

当我们的数组项与defaultDataIdFromObject不兼容时,这就是我们实际想要的行为。

因此,解决方案是手动配置这些唯一标识符,并将dataIdFromObject选项传递给InMemoryCache中的ApolloClient构造函数。以下内容适用于我,因为我的所有对象都使用_id并具有__typename。

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache({
    dataIdFromObject: o => (o._id ? `${o.__typename}:${o._id}`: null),
  })
});

答案 1 :(得分:4)

将此放入App.js

cache: new InMemoryCache({
    dataIdFromObject: o => o.id ? `${o.__typename}-${o.id}` : `${o.__typename}-${o.cursor}`,
  })

答案 2 :(得分:0)

我认为应避免使用其他两个答案中的方法,而应采用以下方法:

实际上,这很简单。要了解其工作原理,只需按如下所示记录obj:

const Section = ({ handleClick }) => {
  return (
    <div className="section">
      Section
      <Tile handleClick={handleClick} number="1" />
      <Tile handleClick={handleClick} number="2" />
      <Tile handleClick={handleClick} number="3" />
    </div>
  );
};

const Tile = ({ handleClick, number }) => {
  return (
    <div className="tile" onClick={() => handleClick(number)}>
      tile {number}
    </div>
  );
};

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      modalOpen: false,
      openedBy: ""
    };
  }

  openModal = title => {
    this.setState({
      modalOpen: true,
      openedBy: title
    });
  };

  closeModal = () => {
    this.setState({
      modalOpen: false,
      openedBy: ""
    });
  };

  render() {
    return (
      <div className="App">
        <div>ModalOpen = {this.state.modalOpen.toString()}</div>
        <div>Opened by = {this.state.openedBy}</div>
        <Section handleClick={this.openModal} />
        <a href="#" onClick={this.closeModal}>
          Close modal
        </a>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));

如果遇到此问题,您将在日志中看到id为空。

请注意已记录的“ obj”。它将为返回的每个对象打印。

这些对象是Apollo试图从中获取唯一ID的对象(您必须告诉Apollo,该对象中的哪个字段对于从GraphQL返回的“项目”数组中的每个对象都是唯一的-与传递唯一方法相同呈现DOM元素时使用“地图”或其他迭代时,React中“键”的值)。

From Apollo dox

  

默认情况下,InMemoryCache将尝试使用常见的   id和_id的唯一标识符的唯一标识符(如果存在)   以及对象上的__typename。

因此,请查看'defaultDataIdFromObject'使用的记录的'obj'-如果看不到'id'或'_id',则应在对象中提供每个对象唯一的字段。

我将示例从Apollo dox更改为涉及三种情况,当您可能提供了不正确的标识符时-这是针对您具有多个GraphQL类型的情况:

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>

我希望您现在看到其他两个答案中的方法都是有风险的。

您始终具有唯一的标识符。只需让对象知道该字段即可轻松帮助阿波罗。如果未通过添加查询定义来获取,则将其添加。