如何将全局状态数据处理到 Redux 中的深层嵌套组件中?
How to handle global state data into deeply nested components in Redux?
假设您有一个使用此组件结构的聊天应用程序:
<ChatApp>
<CurrentUserInfo>...</CurrentUserInfo>
<ChatsPanel>...</ChatsPanel>
<SelectedChatPanel>
<MessagesList>
<MessageBaloon>
<MessageText></MessageText>
<MessageUserHead></MessageUserHead>
</MessageBaloon>
...
</MessagesList>
<SelectedChatPanel>
</ChatApp>
Redux 状态如下:
{
currentUser: ...,
chatsList: ...,
selectedChatIndex: ...,
messagesList: [ ... ]
}
如何使 <MessageUserHead>
组件(将为每条消息呈现当前用户缩略图)提供当前用户信息,而不必从根组件一路传递到所有中间组件?
以同样的方式,如何在不暴露整个状态对象的情况下使组件树中的每个 presentational/dumb 组件都可以使用当前语言、主题等信息?
对于所有 "dumb" 组件的全局信息,您可以使用 react contexts。
一个人为的例子
// redux aware component
var ChatApp = React.createClass({
childContextTypes: {
language: React.PropTypes.string
},
getChildContext: function() {
// or pull from your state tree
return {language: "en"};
},
...
}
// dumb components
var ExDumb = React.createClass({
contextTypes: {
language: React.PropTypes.string
},
render: function() {
var lang = this.context.language;
return ( <div /> );
}
});
为了回应评论,redux 在他们的 react-redux 库中使用了这个context approach。
更抽象地说,为了在 React 之外使用,您可以在状态树上使用某种 pluck 或 selector function,而 return 只是哑组件所需的全局状态的一个子集。
(更新:在选项 4 上花了一些时间,我个人认为这是可行的方法。我发布了一个库,react-redux-controller 围绕这种方法构建。)
我知道有几种方法可以从根组件获取数据,一直到叶组件,再通过中间的分支。
道具链
Redux 文档,在使用 react-redux, suggest 通过 props
将数据传递到整个分支链的上下文中。我不赞成这个想法,因为它将所有中间分支组件耦合到今天的应用程序结构。从好的方面来说,您的 React 代码将相当纯净,并且仅在顶层耦合到 Redux 本身。
所有组件中的选择器
或者,您可以使用 connect
使 Redux 存储中的数据可用,而不管您在组件树中的哪个位置。这将您的组件彼此分离,但它会将 everything 耦合到 Redux。我会注意到 Redux 的主要作者 not necessarily opposed 支持这种方法。而且它的性能可能更高,因为它可以防止由于 props
他们实际上并不关心的变化而重新渲染中间组件。
反应 children
我没有想太多关于以这种方式做事,但是你可以在最高级别将你的整个应用程序结构描述为嵌套组件,将 props 直接传递给远程后代,并使用 children
在分支级别渲染注入的组件。然而,在极端情况下,这会使您的容器组件变得非常复杂,尤其是对于具有不止一种类型的子组件的中间组件。由于这个原因,不确定这是否真的可行。
React 上下文
正如@mattclemens 首次提到的,您可以使用实验性 context api 来解耦中间组件。是的,是 "experimental"。是的,React 团队似乎并不喜欢它。但请记住,这正是 Redux 的 connect
用来注入 dispatch
和来自选择器的道具。
我认为它取得了很好的平衡。组件保持解耦,因为分支组件不需要关心后代的依赖关系。如果只在根使用 connect
设置上下文,那么所有的后代只需要耦合到 React 的上下文 API,而不是 Redux。组件可以自由地重新排列,只要某个祖先正在设置所需的 context
属性。如果设置 context
的唯一组件是根组件,则这是平凡的。
React 团队将使用 context
与全局变量进行比较,但这感觉有点夸张。对我来说,它似乎更像是依赖注入。
假设您有一个使用此组件结构的聊天应用程序:
<ChatApp>
<CurrentUserInfo>...</CurrentUserInfo>
<ChatsPanel>...</ChatsPanel>
<SelectedChatPanel>
<MessagesList>
<MessageBaloon>
<MessageText></MessageText>
<MessageUserHead></MessageUserHead>
</MessageBaloon>
...
</MessagesList>
<SelectedChatPanel>
</ChatApp>
Redux 状态如下:
{
currentUser: ...,
chatsList: ...,
selectedChatIndex: ...,
messagesList: [ ... ]
}
如何使 <MessageUserHead>
组件(将为每条消息呈现当前用户缩略图)提供当前用户信息,而不必从根组件一路传递到所有中间组件?
以同样的方式,如何在不暴露整个状态对象的情况下使组件树中的每个 presentational/dumb 组件都可以使用当前语言、主题等信息?
对于所有 "dumb" 组件的全局信息,您可以使用 react contexts。
一个人为的例子
// redux aware component
var ChatApp = React.createClass({
childContextTypes: {
language: React.PropTypes.string
},
getChildContext: function() {
// or pull from your state tree
return {language: "en"};
},
...
}
// dumb components
var ExDumb = React.createClass({
contextTypes: {
language: React.PropTypes.string
},
render: function() {
var lang = this.context.language;
return ( <div /> );
}
});
为了回应评论,redux 在他们的 react-redux 库中使用了这个context approach。
更抽象地说,为了在 React 之外使用,您可以在状态树上使用某种 pluck 或 selector function,而 return 只是哑组件所需的全局状态的一个子集。
(更新:在选项 4 上花了一些时间,我个人认为这是可行的方法。我发布了一个库,react-redux-controller 围绕这种方法构建。)
我知道有几种方法可以从根组件获取数据,一直到叶组件,再通过中间的分支。
道具链
Redux 文档,在使用 react-redux, suggest 通过 props
将数据传递到整个分支链的上下文中。我不赞成这个想法,因为它将所有中间分支组件耦合到今天的应用程序结构。从好的方面来说,您的 React 代码将相当纯净,并且仅在顶层耦合到 Redux 本身。
所有组件中的选择器
或者,您可以使用 connect
使 Redux 存储中的数据可用,而不管您在组件树中的哪个位置。这将您的组件彼此分离,但它会将 everything 耦合到 Redux。我会注意到 Redux 的主要作者 not necessarily opposed 支持这种方法。而且它的性能可能更高,因为它可以防止由于 props
他们实际上并不关心的变化而重新渲染中间组件。
反应 children
我没有想太多关于以这种方式做事,但是你可以在最高级别将你的整个应用程序结构描述为嵌套组件,将 props 直接传递给远程后代,并使用 children
在分支级别渲染注入的组件。然而,在极端情况下,这会使您的容器组件变得非常复杂,尤其是对于具有不止一种类型的子组件的中间组件。由于这个原因,不确定这是否真的可行。
React 上下文
正如@mattclemens 首次提到的,您可以使用实验性 context api 来解耦中间组件。是的,是 "experimental"。是的,React 团队似乎并不喜欢它。但请记住,这正是 Redux 的 connect
用来注入 dispatch
和来自选择器的道具。
我认为它取得了很好的平衡。组件保持解耦,因为分支组件不需要关心后代的依赖关系。如果只在根使用 connect
设置上下文,那么所有的后代只需要耦合到 React 的上下文 API,而不是 Redux。组件可以自由地重新排列,只要某个祖先正在设置所需的 context
属性。如果设置 context
的唯一组件是根组件,则这是平凡的。
React 团队将使用 context
与全局变量进行比较,但这感觉有点夸张。对我来说,它似乎更像是依赖注入。