【问题标题】:React Apollo and Redux: Combine custom reducers with Apollo stateReact Apollo 和 Redux:将自定义 reducer 与 Apollo 状态相结合
【发布时间】: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]

【问题讨论】:

    标签: node.js reactjs react-redux graphql react-apollo


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

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

      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 可以更好地解决原始问题中描述的用例。
        猜你喜欢
        • 1970-01-01
        • 2018-05-02
        • 2017-07-14
        • 2019-02-11
        • 1970-01-01
        • 2021-07-30
        • 2021-07-18
        • 2018-06-08
        • 2019-06-17
        相关资源
        最近更新 更多