我的应用程序本质上是一个包含过滤器和更改布局的按钮的列表。
目前,我我正在使用三个组件:
<list />
,< Filters />
和<TopBar />
,现在很明显,当我更改< Filters />
中的设置时,我想触发<list />
中的某种方法来更新我的视图。彼此,还是我需要可以在其中进行更改的某种全局数据模型?#1 楼
最好的方法取决于您计划如何安排这些组件。现在想到的一些示例场景:<Filters />
是<List />
的子组件。父组件<Filters />
和<List />
完全位于单独的根组件中。可能还有其他我没有想到的情况。如果您的不适合这些,请告诉我。下面是一些有关如何处理前两种情况的非常粗糙的示例:
方案1 #br
您可以将处理程序从
<Filters />
传递给<List />
会在<List />
事件上被调用,以使用当前值过滤列表。#1的JSFiddle→场景#2
与场景#1类似,但父组件是将处理函数传递给
<Filters />
的组件,会将经过过滤的列表传递给onChange
。我更喜欢这种方法,因为它使/** @jsx React.DOM */
var Filters = React.createClass({
handleFilterChange: function() {
var value = this.refs.filterInput.getDOMNode().value;
this.props.updateFilter(value);
},
render: function() {
return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
}
});
var List = React.createClass({
getInitialState: function() {
return {
listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
nameFilter: ''
};
},
handleFilterUpdate: function(filterValue) {
this.setState({
nameFilter: filterValue
});
},
render: function() {
var displayedItems = this.state.listItems.filter(function(item) {
var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
return (match !== -1);
}.bind(this));
var content;
if (displayedItems.length > 0) {
var items = displayedItems.map(function(item) {
return <li>{item}</li>;
});
content = <ul>{items}</ul>
} else {
content = <p>No items matching this filter</p>;
}
return (
<div>
<Filters updateFilter={this.handleFilterUpdate} />
<h4>Results</h4>
{content}
</div>
);
}
});
React.renderComponent(<List />, document.body);
与<Filters />
分离。 / pre> 场景#3
当组件无法在任何类型的父子关系之间进行通信时,文档建议设置一个全局事件系统。
评论
#2的好处是,它们仅依赖于将prop传递给每个组件的父对象:作为
– Michael LaCroix
2014年1月24日,1:10
@woutr_be不确定是否满足您的要求,但是在类似情况下,有时我们会使用以下两个函数在子组件和父组件之间进行通信:-listenTo:function(eventName,eventCallback){$( window.document).bind(eventName,eventCallback);} triggerEvent:function(eventName,params){$ .event.trigger(eventName,params);}希望能有所帮助! (对不起,格式化效果更好)
–5122014009
2014年1月24日5:08
对于方案3,是否有推荐的方法?通过生成自定义综合事件,是否有任何文档或示例?我没有在主要文档中找到任何东西。
– pwray
2014年1月24日,11:23
场景2很有道理...直到您不得不危害设计(如果只有布局),那么您便意识到了EventHub / PubSub的必要性。
–科迪
16年5月16日在18:31
场景#3链接已失效,现在重定向到一个不相关的React docs页面。
–记者
19年10月2日在21:36
#2 楼
有多种方法可以使组件进行通信。有些可以适合您的用例。这是我发现有用的一些列表。反应
父母/子女直接沟通
const Child = ({fromChildToParentCallback}) => (
<div onClick={() => fromChildToParentCallback(42)}>
Click me
</div>
);
class Parent extends React.Component {
receiveChildValue = (value) => {
console.log("Parent received value from child: " + value); // value is 42
};
render() {
return (
<Child fromChildToParentCallback={this.receiveChildValue}/>
)
}
}
此处子组件将调用父级提供的带有值的回调,并且父级将能够获取父级中子级提供的值。
如果构建功能/ app的/ page,最好是由一个父级来管理回调/状态(也称为
container
或smart component
),并且所有子级都是无状态的,只向父级报告情况。这样,您可以轻松地将父状态共享给需要它的任何子级。上下文
React Context允许将状态保留在根组件层次结构,并且可以轻松地将此状态注入到嵌套很深的组件中,而不必麻烦将prop传递给每个中间组件。但是React 16.3中提供了新的API。
const AppContext = React.createContext(null)
class App extends React.Component {
render() {
return (
<AppContext.Provider value={{language: "en",userId: 42}}>
<div>
...
<SomeDeeplyNestedComponent/>
...
</div>
</AppContext.Provider>
)
}
};
const SomeDeeplyNestedComponent = () => (
<AppContext.Consumer>
{({language}) => <div>App language is currently {language}</div>}
</AppContext.Consumer>
);
消费者正在使用render prop / children功能模式
查看此博客文章以获取更多详细信息。
/>在React 16.3之前,我建议使用提供类似API的react-broadcast,并使用以前的上下文API。
门户网站
使用门户当您希望将2个组件保持在一起以使它们与简单的功能进行通信时(例如在正常的父/子中),但是您不希望这2个组件在DOM中具有父/子关系,因为视觉/它所暗示的CSS约束(例如z-index,opacity ...)。
在这种情况下,您可以使用“门户”。有多种使用门户的反应库,通常用于模式,弹出窗口,工具提示...
请考虑以下内容:
<div className="a">
a content
<Portal target="body">
<div className="b">
b content
</div>
</Portal>
</div>
可以在
reactAppContainer
内部呈现时,以下DOM:<body>
<div id="reactAppContainer">
<div className="a">
a content
</div>
</div>
<div className="b">
b content
</div>
</body>
此处有更多详细信息
插槽
您在某处定义插槽,然后从其他位置填充插槽渲染树。
import { Slot, Fill } from 'react-slot-fill';
const Toolbar = (props) =>
<div>
<Slot name="ToolbarContent" />
</div>
export default Toolbar;
export const FillToolbar = ({children}) =>
<Fill name="ToolbarContent">
{children}
</Fill>
这与门户有点相似,除了填充的内容将在您定义的插槽中渲染,而门户通常会渲染新的dom节点(通常子文档)
检查react-slot-fill库
事件总线
如React文档中所述:
要在两个没有父子关系的组件之间进行通信,可以设置自己的全局事件系统。在收到事件时,订阅componentDidMount()中的事件,在componentWillUnmount()中取消订阅,并在收到事件时调用setState()。您可以只创建一个侦听器数组,并且在事件发布时,所有侦听器都将接收该事件。或者,您可以使用EventEmitter或PostalJs之类的东西。Flux基本是事件总线,除了事件接收者是商店。这类似于基本事件总线系统,除了状态是在React之外进行管理的。
对我来说,Redux是最接近事件源的Flux实现,它受益于许多事件源优势,例如时间旅行能力。它并不严格地与React链接,也可以与其他功能视图库一起使用。
游标
游标来自ClojureScript / Om,广泛用于React项目。它们允许管理React外部的状态,并允许多个组件对状态的同一部分具有读/写访问权限,而无需了解任何有关组件树的信息。
存在许多实现,包括ImmutableJS,React-cursors和Omniscient
Edit 2016:人们似乎认为游标在较小的应用程序上可以正常工作,但在复杂的应用程序上无法很好地扩展。 Om Next不再具有游标(虽然Om最初引入了该概念)
Elm体系结构
Elm体系结构是提议由以下人员使用的体系结构榆木语言。即使Elm不是ReactJS,Elm架构也可以在React中完成。
Redux的作者Dan Abramov使用React实现了Elm架构的实现。
Redux和Elm都很出色,并且倾向于在前端启用事件源概念,都允许进行时间旅行调试,撤消/重做,重播...
Redux和榆树是榆树在状态管理方面往往要严格得多。在Elm中,您不能具有本地组件状态或挂接/卸载挂接,并且所有DOM更改都必须由全局状态更改触发。 Elm体系结构提出了一种可伸缩的方法,该方法允许处理单个不可变对象内的所有状态,而Redux提出的方法邀请您在单个不可变对象中处理状态的大多数。 Elm的模型非常优雅,并且该架构允许在大型应用程序上很好地扩展,但实际上可能很困难或需要更多样板来完成简单的任务,例如在安装后将焦点放在输入上,或与现有库集成界面(即JQuery插件)。相关问题。
此外,Elm体系结构涉及更多代码样板。编写起来并不是那么冗长或复杂,但我认为Elm体系结构更适合于静态类型的语言。
FRP可用于生成FRP流以处理组件之间的通信。
您可以尝试使用Rx-React
我认为使用这些库与使用ELM语言提供的信号非常相似。
CycleJS框架不使用ReactJS,而是使用vdom。它与Elm架构有很多相似之处(但在现实生活中更容易使用,因为它允许使用vdom钩子),并且广泛使用RxJ而不是函数,如果您想将FRP与反应CycleJs Egghead视频很高兴理解它的工作原理。 .async / ClojureScript),但您也可以在JS-CSP的javascript中使用它们。
传奇是来自DDD / EventSourcing / CQRS世界的后端概念,也称为“流程管理器”。
它正在由redux-saga项目推广,主要是作为redux-thunk的替代品,用于处理副作用(例如API调用等)。当前,大多数人认为它仅用于副作用,但实际上更多是关于去耦组件。
它完全是对Flux架构(或Redux)的补充,而不是全新的通信系统,因为传奇最终会发出Flux动作。这样做的想法是,如果您有widget1和widget2,并且希望将它们解耦,则无法从widget1触发针对widget2的操作。因此,您使widget1仅触发以自身为目标的动作,并且传奇事件是侦听widget1动作的“后台进程”,并且可以调度以widget2为目标的动作。传奇是2个小部件之间的耦合点,但小部件保持脱耦状态。 />
如果要查看使用这些不同样式的同一小应用程序的示例,请检查此存储库的分支。
从长远来看,我不知道什么是最佳选择,但我真的很喜欢Flux看起来像事件源。
如果您不了解事件源的概念,请看一下这个非常有教育意义的博客:用apache Samza将数据库内翻,这是理解Flux为什么不错的必读(但这也可能适用于FRP)。
我认为社区同意最有前途的Flux实施是Redux,由于热重装,它将逐步提供非常富有成效的开发人员体验。令人印象深刻的现场编码ala Bret Victor的Inventor on Principle视频是可能的!
评论
极好的答案Seb!
–阿里·加贾尼(Ali Gajani)
19 Mar 6 '19 at 4:14
#3 楼
好的,有几种方法可以做到,但是我只想专注于使用Redux来存储,这使您在这种情况下的生活更加轻松,而不是仅针对这种情况提供快速解决方案,使用纯React最终会导致真正的大型应用程序以及组件之间的通信随着应用程序的增长而变得越来越难...那么Redux会为您做什么呢?只要您需要在应用程序中的不同位置使用数据,就可以使用它。
基本上,Redux的想法最初来自于流量,但是它发生了一些根本性的变化,包括拥有一个真实来源的概念通过仅创建一个商店...
查看下图,查看Flux和Redux之间的一些区别...
考虑如果您的应用程序需要组件之间的通信,请从一开始就在您的应用程序中使用Redux ...
也请阅读这些文字Redux Documentation的以下文章可能对您有所帮助:
随着JavaScript单页应用程序的要求变得越来越复杂,我们的代码必须管理的状态比>以前。此状态可以包括服务器响应和缓存的数据,以及尚未持久保存到
服务器的本地创建的数据。 UI状态的复杂性也在增加,因为我们需要管理活动路线,选定的选项卡,微调器,分页控件等。
管理这种不断变化的状态很难。如果一个模型可以更新另一个模型,则视图可以更新一个模型,从而更新另一个
模型,这又可能导致另一个视图更新。在某些时候,您不再了解应用程序中发生的事情,因为您已失去对状态的时间,原因和方式的控制。当系统
是不透明且不确定的,因此很难重现错误或添加新功能。
好像还不够糟糕,请考虑一下新的需求成为常见的需求产品开发。作为开发人员,我们被期望在执行路由转换之前处理乐观更新,服务器端呈现,获取数据等。我们发现自己
试图管理一种以前从未处理过的复杂性
,我们不可避免地问一个问题:该放弃了吗?
答案是否定的。
这种复杂性很难处理,因为我们混用了两个很难理解的概念。
/>异步性。我称他们为Mentos和可乐。两者在分隔方面可能很棒,但是它们一起造成混乱。像React
这样的库试图通过消除异步和直接DOM操作来解决视图层中的此问题。但是,管理数据的状态由您自己决定。这是Redux进入的地方。
遵循Flux,CQRS和事件源的步骤,Redux
试图通过对方式和时间施加某些限制来使状态突变可预测更新可能会发生。这些限制
反映在Redux的三个原则中。
评论
Redux如何提供帮助?如果我有一个日期选择器的模态(作为一个组件),并且可以从同一页面上的多个组件加载该组件,那么日期选择器组件将如何知道要分派给Redux的Action?这是问题的本质,将一个组件中的动作链接到另一个组件而不是其他任何组件。 (考虑到日期选择器本身是模式组件本身中的一个很深的组件)
– vsync
18-10-9在12:34
@vsync不要将reudx视为单个静态值,redux实际上可以具有对象,数组...因此您可以将它们保存为对象或数组或存储中的任何内容,它可以是mapdispatchtoprops,每个保存在对象数组中像:[{name:“ picker1”,value:“ 01/01/1970”},{name:“ picker2”,value:“ 01/01/1980”}],然后在parent中使用mapstatetoprops并将其向下传递给每个组件或您想要的任何位置,不确定是否能回答您的问题,但是看不到代码...如果它们在单独的组中,您也可以提出更多详细信息...但是这完全取决于您要如何分组他们..
– Alireza
18-10-9在14:26
问题不是关于redux以及您可以存储什么,而是如何将操作深入传递到触发它的原因。深度组件如何知道确切需要触发什么?因为在该示例中,我给出了一个通用组件,该组件应根据情况触发特定的减速器,因此它可以是不同的减速器,因为datepicker模态可用于任何组件。
– vsync
18-10-9在14:41
#4 楼
这就是我的处理方法。假设您有一个
评论
是所有三个同级组件,还是一个内部组件?它们都是三个组成部分,我已经重新安排了我的应用程序,以便它们现在都具有相同的父级,可以为它们提供数据。
在这里可以使用flux或pubsub模式。根据react文档中的文档,他们留下了一个含糊的句子:“对于两个没有父子关系的组件之间的通信,您可以设置自己的全局事件系统。” facebook.github.io/react/tips / ...
@BingeBoy是对的Flux是编写reactjs应用程序的好方法,可以处理数据流,许多组件共享数据的问题。
如果您不想进入Flux或Redux,这是一篇关于该主题的很棒的文章andrewhfarmer.com/component-communication