【发布时间】:2016-04-01 14:17:20
【问题描述】:
我对此有一个概念证明,这似乎可行,但我的一部分想知道这是否真的是一个好主意,以及是否有使用 Redux 之类的东西或替代策略的更好的解决方案。
问题
基本上,我的整个应用程序都有一个基本的 React 组件,它有一堆你可能期望的典型组件,页眉、菜单、页脚等。
在我的树的更下方(更远)我有一个组件,如果我可以在我的标题组件中安装一个新的菜单项,那将是很棒的。标头组件当然位于我的应用程序的顶部,因此访问被拒绝。
这只是一个这样的例子,但这是我从多个角度遇到的问题。
我的疯狂解决方案
我研究了使用 React 的上下文来公开允许子组件声明它们希望出现在标题中的任何其他元素的函数。
在尝试了这个概念之后,我最终将它重构为一个非常通用的解决方案,本质上是一个 React Element 消息传递系统。此解决方案分为三个部分。
1.提供者
单实例组件与 Redux 的 Connect 组件非常相似。她本质上是接收和传递消息的引擎。她的基本结构(以上下文为中心)是:
class ElementInjectorProvider extends Component {
childContextTypes: {
// :: (namespace, [element]) -> void
produceElements: PropTypes.func.isRequired,
// :: (namespace, [element]) -> void
removeElements: PropTypes.func.isRequired,
// :: (listener, namespace, ([element]) -> void) -> void
consumeElements: PropTypes.func.isRequired,
// :: (listener) -> void
stopConsumingElements: PropTypes.func.isRequired,
}
/* ... Implementation ... */
}
2.制作人
更高阶的组件。每个实例都可以通过produceElements 上下文项“生成”元素,为特定命名空间提供元素,然后通过removeElements 删除元素(在组件卸载的情况下)。
function ElementInjectorProducer(config) {
const { namespace } = config;
return function WrapComponent(WrappedComponent) {
class ElementInjectorConsumerComponent {
contextTypes = {
produceElements: PropTypes.func.isRequired,
removeElements: PropTypes.func.isRequired
}
/* ... Implementation ... */
}
return ElementInjectorProducerComponent;
};
}
3.消费者
更高阶的组件。每个实例都配置为“监视”附加到给定名称空间的元素。它使用consumeElements通过回调函数注册“开始”监听,使用stopConsumingElements注销消费。
function ElementInjectorConsumer(config) {
const { namespace } = config;
return function WrapComponent(WrappedComponent) {
class ElementInjectorConsumerComponent {
contextTypes = {
consumeElements: PropTypes.func.isRequired,
stopConsumingElements: PropTypes.func.isRequired
}
/* ... Implementation ... */
}
return ElementInjectorConsumerComponent;
};
}
这是对我打算做的事情的粗略概述。基本上,当您查看它时,它是一个消息传递系统。也许可以进一步抽象。
我已经在玩 redux,猜猜 Redux 有什么用处?所以我不禁觉得,虽然这对我有用,但也许这不是一个好的设计,我无意中站在了 Redux 的脚趾上,或者产生了一个普遍的反模式。
我想我没有直接使用 Redux 的唯一原因是因为我正在生成元素,而不是简单的状态。我可以沿着创建元素描述符对象的路线走,然后通过 Redux 将其传递下去,但这本身就很复杂。
有什么智慧之言吗?
第 1 号更新
对上述内容的一些补充说明。
这允许我在我的完整组件树上上下,甚至从左到右注入元素。我知道大多数 React Context 示例都描述了将数据从祖父组件注入到孙子组件中。
另外,我希望上述实现能够从开发人员那里抽象出任何有关上下文使用的知识。事实上,我很可能会使用这些 HOFS 来创建特定于用例且更加明确的额外包装器。
即
消费者实现:
<InjectableHeader />
生产者实现:
InjectIntoHeader(<FooButton />)(FooPage)
我认为这很明确,也很容易理解。我确实喜欢我可以在它最关心的地方创建按钮,这使我能够与同行建立更牢固的关系。
我也明白 redux flow 可能是正确的想法。感觉就像我让自己变得更加困难 - 我不禁认为这种技术可能有一些优点。
这有什么特别的坏主意吗?
第 2 条更新
好的,我现在确信这是一个坏主意。我基本上破坏了应用程序的可预测性,并取消了单向数据模型提供的所有好处。
我仍然不相信使用 Redux 是这种情况下的最佳解决方案,我已经构想了一个更明确的单向解决方案,它使用了上面的一些概念,但没有任何上下文魔法。
如果我认为可行,我会发布任何解决方案作为答案。如果做不到这一点,我会去 Redux 并责备自己没有早点听你们的。
其他示例
这里有一些其他项目/想法试图使用各种技术来解决相同(ish)的问题:
https://joecritchley.svbtle.com/portals-in-reactjs
【问题讨论】:
-
我更喜欢使用 Redux,如果还有其他原因,因为一致性,这对以后维护代码有很大帮助。
-
我可以理解最好保持单一模式,但我不知道我的生产者/消费者模式是否会增加那么多额外的维护开销。我必须将两个字符串匹配在一起(查找全部)以查看生产的产品在哪里被消耗。使用 Redux,我必须执行操作,然后检查正在调用哪些 reducer,然后通过 state 进行选择,当组件卸载时,我需要对我的 state 执行清理过程。虽然,即使说了所有这些,我认为你可能仍然是对的。我只是想尝试进一步探索,看看是否还有更多内容。
标签: javascript reactjs redux react-redux