React Apollo 和 Redux:将自定义 reducer 与 Apollo 状态相结合

Posted

技术标签:

【中文标题】React Apollo 和 Redux:将自定义 reducer 与 Apollo 状态相结合【英文标题】:React Apollo and Redux: Combine custom reducers with Apollo state 【发布时间】:2017-12-12 00:41:50 【问题描述】:

当使用 react-apollo 和 redux 时,来自服务器的数据缓存在 apollo 键下的 redux 存储中:


    apollo: // results from Apollo queries

创建自己的 reducer 和相应的状态树后,您的 store 可能如下所示:


    apollo:  ... ,
    ui: 
        selectedItem: 2;
        showItems: 6
    

假设有一个动作改变了ui.selectedItem 的值,并且这个值是埋在apollo 对象中某处的项目的索引。

使用reducer composition,我们ui 状态的reducer 将无法访问Apollo 状态下的任何内容。

在我们的组件中,我们可以通过mapStateToProps 订阅ui 状态的更改,并且我们可以使用react-apollo GraphQL 容器(或@graphql 装饰器)将Apollo 结果映射到道具。

但是我们如何使用根据 Apollo 结果和应用程序中的其他状态计算的值来更新我们的商店?

例如,如果 apollo.item.list 是一个项目数组,而 ui.selectedItem 是您要在该列表中定位的索引 - 商店如何包含以下值:

apollo.item.list[ui.selectedItem]

【问题讨论】:

【参考方案1】:

不是您问题的真正答案,但我发现 ReactQL 是一个很棒的入门工具包,可以为您处理所有这些事情。它具有 Apollo+React+Redux 的所有样板,包括自定义 Redux 减速器。看看他们是怎么做到的。

【讨论】:

【参考方案2】:

我的解决方案是通过mapProps 一起使用recompose 到compose 商店和阿波罗州:

import  mapProps, compose  from ‘recompose’;

const mapStateToProps = state => (
  selectedItem: state.ui.selectedItem
)

const mapDataToProps = 
  props: ( data ) => ( list: data.item.list )


const selectProps = mapProps(( selectedItem, list) => (
  item: list[selectedItem] 
))

export default compose(
  connect(mapStateToProps, ),
  graphql(MY_QUERY, mapDataToProps),
  selectProps
)(MyComponent);

这还允许我们使用branch 检查graphql 的加载状态,因此在数据可用之前我们不会运行selectProps

感谢Daniel Rearden's answer 为我指明了正确的方向。

【讨论】:

【参考方案3】:

你是对的——使用 reducer 组合,每个 reducer 不应该访问其他状态。问题是为什么您甚至需要您的 store 包含 apollo.item.list[ui.selectedItem] 的单独值。

大概你需要这个值来在你的容器中呈现正确的项目。要计算它,您只需要在 mapStateToProps 中调用适当的选择器。传入整个状态,得到你需要的值。一个简化的例子:

const mapStateToProps = state => (
  itemToShow: itemSelector(state)
)

const itemSelector = ( apollo, ui ) => apollo.item.list[ui.selectedItem]

您需要将您的容器包装在 connectgraphql HOC 中以使其工作,尽管据我所知,无论您是先将其包装在一个或另一个中似乎都没有事情。您还可以考虑使用reselect,它非常适合此类派生数据。

编辑:由于 Apollo 的存储块并非真正设计为如上所述使用,您可能会发现将其分为两个步骤更容易:首先,分配 ui.selectedItem到 mapStateToProps 中的一个道具。然后,如果您使用 graphql HOC 包装您的容器,您可以像这样引用该道具:

const mapStateToProps = state => ( selectedItem );
const mapDataToProps = ( ownProps:  selectedItem , data ) => (
  itemToShow: data.item.list[selectedItem]
);
const ContainerWithData = graphql(myQuery,  props: mapDataToProps )(MyContainer);

export default connect(mapStateToProps)(ContainerWithData);

或者您可以使用 apollo-react 的 compose 来获得更简洁的方法。

【讨论】:

谢谢,这是有用的输入。我喜欢第一个解决方案,因为它有 memoization 选项和可重用的选择器(我可能需要在多个容器中使用它 - 因此希望首先存储它)。但正如你所说,这并不是使用 Apollo 的真正可靠方式。第二个选项似乎在 graphql 容器中投入了大量工作,并且似乎不能跨容器重用? 另外,第二个选项似乎只在数据更改时才有效。如果此后 selectedItem 状态发生变化,itemToShow 值不会更新? 嗨丹尼尔,我们可以直接在重新选择选择器中访问apollo.item.list。如果apollo.item 更新,选择器会重新运行吗? @Anirudha 这个问题已经过时了。 Apollo 不再使用引擎盖下的 redux 存储。使用 Apollo local state 可以更好地解决原始问题中描述的用例。

以上是关于React Apollo 和 Redux:将自定义 reducer 与 Apollo 状态相结合的主要内容,如果未能解决你的问题,请参考以下文章

React GraphQL Apollo 和 Redux。如何查询数据并将其置于 store 的 initialState 中?

React Apollo 查询返回后调度 Redux 操作

在 apollo-react 查询后调度 redux 操作

使用 Redux Thunk 和 Apollo Graphql 做出反应 - 如何访问 client.query?

我可以用 redux 提供者和 apollo 提供者包装我的应用程序吗?

如何在 React Native 中正确地将 Apollo 客户端连接到 Redux