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]
您需要将您的容器包装在 connect
和 graphql
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 中?
使用 Redux Thunk 和 Apollo Graphql 做出反应 - 如何访问 client.query?