问题 如何在Redux中将全局状态数据处理为深层嵌套组件?


所以说你有这个组件结构的聊天应用程序:

<ChatApp>
  <CurrentUserInfo>...</CurrentUserInfo>
  <ChatsPanel>...</ChatsPanel>
  <SelectedChatPanel>
    <MessagesList>
      <MessageBaloon>
        <MessageText></MessageText>
        <MessageUserHead></MessageUserHead>
      </MessageBaloon>
      ...
    </MessagesList>
  <SelectedChatPanel>
</ChatApp>

和Redux状态一样:

{ 
  currentUser: ...,
  chatsList: ...,
  selectedChatIndex: ...,
  messagesList: [ ... ]
}

您如何将当前用户信息提供给 <MessageUserHead> 组件(将为每条消息呈现当前用户缩略图),而不必通过所有中间组件从根组件一直传递它?

同样,如何在不借助于暴露整个状态对象的情况下,为组件树中的每个表示/哑组件提供当前语言,主题等信息?


4533
2017-12-15 21:11


起源

github.com/vasanthk/react-bits/blob/master/patterns/... 这个主题的好文章 - zloctb


答案:


对于所有“哑”组件的全局信息,您可以使用 反应背景

一个人为的例子

// 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库中。

更具抽象性的是在反应之外使用,你可以使用某种类型的采取或 选择器功能 在状态树上,只返回哑组件所需的全局状态的子集。


4
2017-12-16 01:24



是的,我知道React的上下文,但我有两个问题:第一个React的上下文API是实验性的,它们可能会删除它或显着改变它。其次,我的问题是关于Redux的解决方法,独立于底层实现(React,AngularJS等)。 - Dema
@Dema我试着在我的回答中解决一下。但我会说,不要害怕改变 context。这是一个如此简单的API,它似乎不太可能在任何基本方面发生变化,而且它已经成为React库的基本功能(如react-redux)的核心。 Redux本身并没有为您的问题提供任何答案,因为它根本不关心“组件层次结构”的概念。您可以使用像react-redux这样的绑定,也可以使用自己的管道:) - acjay
这种方法的问题是不再使用道具来解决组件问题 - jujule
@jujule你能澄清一下吗?你说“被解决”是什么意思? - acjay
对不起我的意思是你不能这样做 <ExDumb language="fr"/> 现在,只有在存在破坏可重用性的上下文时,您的组件才能正常工作?也许某种类型的HoC可以通过将上下文传递给道具来帮助使组件可重用... - jujule


(更新:花了一些时间在选项4上,我个人认为这是要走的路。我发布了一个库, 反应-终极版控制器 围绕这种方法建立。)

我知道有几种方法可以从根组件,到叶子组件,中间的分支中获取数据。

道具链

Redux文档,在使用的上下文中 反应-终极版建议 通过整个分支链传递数据 props。我并不赞成这个想法,因为它将所有中间分支组件耦合到今天的应用程序结构中。从好的方面来说,你的React代码会相当纯粹,并且只能在顶层与Redux本身相连。

所有组件中的选择器

或者,你可以使用 connect 无论您在组件树中的位置如何,都可以从Redux存储中获取数据。这会使您的组件彼此分离,但它会耦合 一切 到Redux。我会注意到Redux的主要作者是 不一定反对 这种方法。并且它可能更高效,因为它可以防止由于更改而重新呈现中间组件 props 他们实际上并不关心。

应对 children

我没有想过以这种方式做事,但是你 可以 将最高级别的整个应用程序结构描述为嵌套组件,将props直接传递给远程后代,并使用 children 在分支级别渲染注入的组件。但是,极端情况下,这会使您的容器组件变得非常复杂,尤其是对于具有多个类型子级的中间组件。由于这个原因,不确定这是否真的可行。

反应上下文

正如@mattclemens首先提到的,你可以使用实验 上下文api 解耦你的中间组件。是的,它是“实验性的”。是的,React团队似乎并不喜欢它。但请记住,这正是Redux的原因 connect 用来注射 dispatch 和选择器的道具。

我认为它达到了很好的平衡。组件保持解耦,因为分支组件不需要关心后代的依赖关系。如果你只使用 connect 在根目录下设置上下文,然后所有后代只需要耦合到React的上下文API,而不是Redux。只要某些祖先设置了所需的组件,组件就可以自由重新排列 context 属性。如果设置的唯一组件 context 是根组件,这是非常正确的。

React团队比较使用 context 全局变量,但这感觉有点夸张。这似乎更像是对我的依赖注入。


12
2017-12-16 19:54



更新:已更详细地记录了React上下文 - 现在它已被各种库采用。 facebook.github.io/react/docs/context.html - vasa
@vasa自从我最初编写答案以来,我认为页面没有任何重大变化。有传言称上下文API可能会发生重大变化,但我不知道这是否仍然存在。 - acjay