Redux和Elm之间的主要区别在于Elm对州管理往往更加严格 . 在Elm中,您不能拥有本地组件状态或挂载/卸载挂钩,并且所有DOM更改都必须由全球状态变化引发 . Elm架构提出了一种可扩展的方法,允许在单个不可变对象内处理 ALL 状态,而Redux提出了一种方法,邀请您在单个不可变对象中处理状态的 MOST .
虽然Elm的概念模型非常优雅,并且架构允许在大型应用程序上很好地扩展,但实际上它可能很难或涉及更多样板来实现简单的任务,例如在安装后将焦点放在输入上,或者与现有库集成使用命令式界面(即JQuery插件) . Related issue .
11 回答
奇怪的是没人提到
mobx
. 这个想法类似于redux
. 如果我有一个数据,多个组件订阅它,那么我可以使用此数据来驱动多个组件 .OK, 有几种方法可以做到这一点,但我完全希望专注于使用商店使用 Redux 这使得你的生活更容易适应这些情况,而不是只为这种情况提供快速解决方案,使用纯粹的React将最终陷入困境随着应用程序的增长,真正的大应用程序和 Components 之间的通信变得越来越难......
那么 Redux 为你做什么?
Redux就像应用程序中的本地存储一样,只要您需要在应用程序的不同位置使用数据,就可以使用它 .
基本上,Redux的想法最初来自于flux,但是有一些根本性的变化,包括通过只创建一个商店来拥有一个真实来源的概念......
请看下面的图表,看看 Flux 和 Redux 之间的一些区别......
如果您的应用程序需要组件之间的通信,请考虑从一开始就在您的应用程序中应用 Redux ...
从Redux文档中读取这些单词可能有助于开始:
我看到问题已经回答了,但是如果你想了解更多细节,总共有 3 cases of communication between components :
案例1:家长与儿童的沟通
案例2:儿童与家长的沟通
案例3:与无关的组件(任何组件的任何组件)通信
这是我处理这个问题的方式 .
假设你有 Month 的<select>和 Day 的<select> . 天数取决于所选月份 .
两个列表都由第三个对象(左侧面板)拥有 . 两个<select>也是leftPanel <div>的子元素
这是一个带有回调和LeftPanel组件中的处理程序的游戏 .
要测试它,只需将代码复制到两个单独的文件中并运行index.html . 然后选择一个月,看看天数如何变化 .
dates.js
用于运行左面板组件的HTML index.html
最好的方法取决于您计划如何安排这些组件 . 现在浮现在脑海中的一些示例场景:
<Filters />
是<List />
的子组件<Filters />
和<List />
都是父组件的子项<Filters />
和<List />
完全位于单独的根组件中 .可能还有其他我不想的场景 . 如果你的不适合这些,请告诉我 . 以下是我如何处理前两个场景的一些非常粗略的例子:
场景#1
您可以将处理程序从
<List />
传递到<Filters />
,然后可以在onChange
事件上调用该处理程序以使用当前值过滤列表 .JSFiddle for #1 →
场景#2
与场景#1类似,但父组件将是将处理程序函数传递给
<Filters />
的组件,并将过滤后的列表传递给<List />
. 我更喜欢这种方法,因为它将<List />
与<Filters />
分离 .JSFiddle for #2 →
场景#3
当组件无法在任何类型的父子关系之间进行通信时,documentation recommends setting up a global event system .
如果您想探索组件之间的通信选项并感觉它变得越来越难,那么您可能会考虑采用一种好的设计模式:Flux .
它只是一组规则,定义了如何存储和改变应用程序范围的状态,并使用该状态来呈现组件 .
有许多Flux实现,Facebook's official implementation就是其中之一 . 虽然它被认为是包含大多数样板代码的那个,但它更容易理解,因为大多数事情是明确的 .
其他一些替代方案是flummox fluxxor fluxible和redux .
即使他们不是父子关系,也有这种可能性 - 那就是Flux . 对于名为Alt.JS(使用Alt-Container)的实现非常好(对于我个人而言) .
例如,您可以使用侧栏,该侧栏依赖于组件详细信息中设置的内容 . 组件侧栏与SidebarActions和SidebarStore连接,而Details是DetailsActions和DetailsStore .
你可以像那样使用AltContainer
哪个会保留商店(我可以使用“商店”代替“商店”道具) . 现在,可以是细节,具体取决于路线 . 让我们说/ Details将我们重定向到该视图 . 例如,如果要检查侧边栏元素,则将详细信息设置为将侧边栏元素从X更改为Y.
从技术上讲,它们之间没有任何关系,如果没有变化,就很难做到 . 但这很容易 .
现在让我们来看看DetailsActions . 我们将在那里创造
和DetailsStore
现在,这是魔术开始的地方 .
正如您所看到的,将使用bindListener到SidebarActions.ComponentStatusChanged,如果将使用setSiteComponent将使用它 .
现在在SidebarActions中
我们有这样的事情 . 它将在电话中调度该对象 . 并且如果将使用商店中的setSiteComponent将调用它(您可以在组件中使用,例如在按钮上的onChange期间使用任何东西)
现在我们将在SidebarStore
现在,您可以看到,它将等待DetailsStore . 这是什么意思?或多或少意味着此方法需要等待DetailsStore更新才能更新自身 .
tl; dr One Store正在侦听商店中的方法,并将触发组件操作的操作,组件操作将更新其自己的商店 .
我希望它可以帮到你不知何故 .
当场景是组件无法在任何类型的父子关系之间进行通信时,扩展@MichaelLaCroix的答案,文档建议设置全局事件系统 .
在
<Filters />
和<TopBar />
的情况下没有任何上述关系,可以使用一个简单的全局 Launcher ,如下所示:componentDidMount
- 订阅活动componentWillUnmount
- 取消订阅活动React.js and EventSystem code
EventSystem.js
NotificationComponent.js
有多种方法可以使组件进行通信 . 有些可以适合您的用例 . 这是我发现有用的一些清单 .
反应
家长/孩子直接沟通
这里子组件将使用一个值调用父提供的回调,并且父组件将能够获得父组中子组提供的值 .
如果您构建应用程序的功能/页面,最好让单个父级管理回调/状态(也称为
container
或smart component
),并且所有子级都是无状态的,只向父级报告内容 . 通过这种方式,您可以轻松"share"任何需要它的孩子的父级状态 .上下文
React Context允许在组件层次结构的根目录中保存状态,并且能够轻松地将此状态注入到非常深层嵌套的组件中,而无需将道具传递给每个中间组件 .
到目前为止,上下文是一个实验性功能,但React 16.3中提供了一个新的API .
消费者正在使用render prop / children function pattern
有关详细信息,请查看此blog post .
在React 16.3之前,我建议使用提供非常相似API的react-broadcast,并使用以前的上下文API .
门户网站
当你想要将2个组件靠近在一起以使它们与简单函数通信时使用门户网站,例如普通的父/子,但是你不希望这两个组件在DOM中有父/子关系,因为它暗示的视觉/ CSS约束(如z-index,opacity ......) .
在这种情况下,您可以使用"portal" . 有不同的反应库使用portals,通常用于modals,弹出窗口,工具提示......
考虑以下:
在
reactAppContainer
中渲染时可以生成以下DOM:More details here
老虎机
您在某处定义了一个插槽,然后从渲染树的另一个位置填充插槽 .
这有点类似于门户网站,除了填充的内容将在您定义的插槽中呈现,而门户网站通常会呈现一个新的dom节点(通常是document.body的子节点)
检查react-slot-fill库
活动 Bus
如React _98996中所述:
您可以使用许多东西来设置事件总线 . 您只需创建一个侦听器数组,并且在事件发布时,所有侦听器都将接收该事件 . 或者您可以使用类似EventEmitter或PostalJs的内容
Flux
Flux基本上是一个事件总线,除了事件接收器是商店 . 这类似于基本的事件总线系统,除了在React之外管理状态
原始Flux实现看起来像是试图以hacky方式进行事件采购 .
Redux对我来说是最接近事件采购的Flux实施,这有利于许多事件采购优势,如时间旅行的能力 . 它没有严格链接到React,也可以与其他功能视图库一起使用 .
Egghead的Redux video tutorial非常好,并解释它是如何在内部工作的(它真的很简单) .
游标
游标来自ClojureScript/Om并广泛用于React项目 . 它们允许管理React之外的状态,并允许多个组件对状态的同一部分具有读/写访问权限,而无需了解组件树的任何信息 .
存在许多实现,包括ImmutableJS,React-cursors和Omniscient
Edit 2016 :似乎人们同意游标适用于较小的应用程序,但它在复杂的应用程序上无法很好地扩展 . Om Next不再使用游标(虽然最初引入概念的是Om)
榆树建筑
Elm architecture是一个建议由Elm language使用的架构 . 即使Elm不是ReactJS,Elm架构也可以在React中完成 .
Redux的作者Dan Abramov使用React做了一个榆树架构 .
Redux和Elm都非常棒,并且倾向于在前端提供事件采购概念,包括时间旅行调试,撤销/重做,重播......
Redux和Elm之间的主要区别在于Elm对州管理往往更加严格 . 在Elm中,您不能拥有本地组件状态或挂载/卸载挂钩,并且所有DOM更改都必须由全球状态变化引发 . Elm架构提出了一种可扩展的方法,允许在单个不可变对象内处理 ALL 状态,而Redux提出了一种方法,邀请您在单个不可变对象中处理状态的 MOST .
虽然Elm的概念模型非常优雅,并且架构允许在大型应用程序上很好地扩展,但实际上它可能很难或涉及更多样板来实现简单的任务,例如在安装后将焦点放在输入上,或者与现有库集成使用命令式界面(即JQuery插件) . Related issue .
此外,Elm架构涉及更多代码样板 . 编写并不是那么冗长或复杂,但我认为Elm架构更适合静态类型语言 .
FRP
RxJS,BaconJS或Kefir等库可用于生成FRP流以处理组件之间的通信 .
您可以尝试例如Rx-React
我认为使用这些库非常类似于使用ELM语言提供的signals .
CycleJS框架不使用ReactJS但使用vdom . 它与Elm架构有许多相似之处(但在现实生活中更容易使用,因为它允许vdom挂钩)并且它广泛使用RxJ而不是函数,如果你想使用FRP,它可以成为灵感的良好来源反应 . CycleJs Egghead videos很高兴了解它的工作原理 .
CSP
CSP(Communicating Sequential Processes)目前很流行(主要是因为Go / goroutines和core.async / ClojureScript),但你也可以在带有JS-CSP的javascript中使用它们 .
James Long做了video解释它如何与React一起使用 .
Sagas
传奇是一种来自DDD / EventSourcing / CQRS世界的后端概念,也称为"process manager" . 它正在被redux-saga项目推广,主要是作为redux-thunk的替代品来处理副作用(即API调用等) . 目前大多数人认为它只是副作用的服务,但实际上更多的是解耦组件 .
与全新的通信系统相比,它更像是对Flux架构(或Redux)的称赞,因为saga在最后发出了Flux动作 . 我们的想法是,如果您有widget1和widget2,并且希望它们分离,则无法从widget1触发针对widget2的操作 . 因此,您只使widget1触发针对其自身的操作,并且该传奇是一个侦听widget1操作的“后台进程”,并且可能会调度以widget2为目标的操作 . 传奇是两个小部件之间的耦合点,但小部件仍然是分离的 .
如果你有兴趣看一下my answer here
结论
如果您想使用这些不同的样式查看同一个小应用程序的示例,请检查此repository的分支 .
从长远来看,我不知道什么是最好的选择,但我真的很喜欢Flux看起来像事件采购 .
如果您不了解事件采购概念,请查看这个非常教学的博客:Turning the database inside out with apache Samza,理解为什么Flux很好(但这也适用于FRP)是必读的
我认为社区同意最有希望的Flux实现是Redux,由于热重新加载,它将逐步提供非常高效的开发人员体验 . 令人印象深刻的实时编码ala Bret Victor的Inventing on Principle video是可能的!
我曾经是你现在所处的地方,作为一个初学者,你有时会觉得这样做的反应方式不合适 . 我会尝试解决我现在想到的相同方式 .
国家是沟通的基石
通常它归结为在您指出三个组件的情况下改变此组件中的状态的方式 .
<List />
:可能会显示一个项目列表,具体取决于过滤器<Filters />
:过滤器选项将改变您的数据 .<TopBar />
:选项列表 .为了协调所有这些交互,你需要一个更高的组件让我们称之为App,它会将动作和数据传递给每个组件,所以例如看起来像这样
因此,当调用
setFilter
时,它将影响filteredItem并重新渲染两个组件; . 如果这还不完全清楚,我给你做了一个带复选框的例子,你可以在一个文件中查看:以下代码帮助我设置两个兄弟之间的通信 . 在render()和componentDidMount()调用期间,设置在其父级中完成 . 它基于https://reactjs.org/docs/refs-and-the-dom.html希望它有所帮助 .