在示例中,始终有一个名为“容器”的文件夹和一个名为“组件”的文件夹。 这种分离背后的想法是什么?
是“容器”智能组件,还是路由组件或其他组件? “组件”下的组件是否应该总是愚蠢的?
对于我来说, container
是路由的处理程序,它还会拉取该路由的redux状态。 然后我把自己的状态作为支柱。
例如,
container / properties.jsx
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actions from 'actions/properties';
import Filter from 'components/properties/filter';
import List from 'components/properties/list';
import Pagination from 'components/properties/pagination';
class PropertiesContainer extends Component {
render() {
return (
<section>
<Filter filter={this.props.properties.params.filter} />
<List items={this.props.properties.items} isFetching={this.props.properties.isFetching} />
<Pagination pagination={this.props.properties.params.pagination} />
</section>
);
}
}
function mapState(state) {
const { properties } = state;
return { properties };
}
function mapDispatch(dispatch) {
return {
actions: bindActionCreators(actions, dispatch),
};
}
const Connector = connect(mapState, mapDispatch)(PropertiesContainer);
export default Connector;
例如
组件/属性/ pagination.jsx
import React, { Component } from 'react';
import Pager from 'components/ui/pager';
class Pagination extends Component {
render() {
const { total, offset, limit } = this.props.pagination;
const current = offset / limit;
return (
<Pager total={total} current={current} />
)
}
}
export default Pagination;
从您提供的链接中读取:
"A container does data fetching and then renders its corresponding sub-component. "
我得到的更多感觉是,“容器”实际上是redux中称为“智能组件”的东西……并且路由/页面应该拥有自己的文件夹。
我不确定为什么“容器”和“路由处理程序”有任何关系?
按路线对应用程序的组件进行分组是一种非常常见的做法。 正如@theaqua指出的,使每个路由处理程序/组件成为智能/容器组件也是很常见的。 如果您使用CombineReducers,通常会发现自己沿着相同的路线破坏状态,路线和容器。 因此,它们经常联系在一起。
@ronag我不认为您需要制作容器和页面。 为什么您不能将路由处理程序和redux连接器放在1个位置上? 非常方便。 把事情简单化。
在我的应用程序中,我只有3条路线/页面。 另一方面,我有很多独立的面板/模块。 制作一个巨大的connect
是不可行的,我至少需要按照面板进行拆分。
如果我从状态到道具进行所有映射,并在单个文件中获取所有数据,则将无法维护。 特别是如果我在同一位置提取所有数据。
如果您有3条路线,那么您需要3条路线处理程序。 例如, A.jsx
, B.jsx
和C.jsx
/containers
。
在每个容器中,您拉取部分(不是全部!)redux状态,并绑定动作。 然后,将其传递给类似于我的示例的组件。 这是非常容易维护的,因为我(和我的团队)知道—连接到redux并且动作总是在containers
绑定,它永远不会在components
(这在我的示例中很小并且通常是无状态的)。
我想我理解你的建议。 但是,正如我所说,如果我将所有数据都放在这里,那三个文件将变得非常复杂。 由于我们的原因,基本上是“登录,应用程序,注销”,其中应用程序将包含几乎所有内容。
也许我太偏向于Relay
,其中每个组件都有一个容器来描述要获取的数据以及其外观。
我将尝试您的建议,看看最终结果。
我称components
封装的React组件仅由props驱动,不与Redux对话。 与“哑部件”相同。 无论您的路由器,数据提取库等如何,它们都应保持不变。
我将知道Redux,Router等的containers
React组件称为。它们与应用程序的耦合程度更高。 与“智能组件”相同。
@gaearon :完美。 这就澄清了例子。 谢谢你。
@gaearon因此,“哑组件”是无状态组件,可以使用从React 0.14开始的更简单语法编写,例如:
var Aquarium = (props) => {
var fish = getFish(props.species);
return <Tank>{fish}</Tank>;
};
我对么?
@soulmachine是的。
不必要。 语法不是重点。
“哑巴”组件又称“呈现”组件,它们是通过prop接收所有数据的组件,不了解Redux,仅指定外观,而不指定行为。
@theaqua @gaearon谢谢!
尽管“容器”一词在react / redux术语中已经很流行,但我们还是决定在我正在研究的项目中将它们称为“连接器”,以避免与布局/图形容器混淆。
顺便说一句,如何调用它们在前面这里曾讨论过rackt / react-redux#170。 我将所有内容都保存在components
文件夹中,并将演示文稿更深入地保存在components/presentational
components
文件夹中,我认为这很好,因为除了容器之外,应用程序的其他部分不太可能需要它们。
@gaearon会将dispatch
视为“哑”或“智能”的组件吗? 让叶或中间组件分派操作而不是将事件冒泡回到顶部组件进行分派,这特别有用。
是的,虽然我更喜欢connect()
这样的组件,所以偶尔会有用,所以动作创建者被注入为道具。 你怎么称呼它并不重要:-)
当您构建组件模块时该怎么办? 例如,假设我为导航菜单<NavMenu>
有一个单独的节点模块,我希望人们这样做:
import {NavMenu} from 'my-redux-aware-components';
export function myPage(props) {
return (<div><NavMenu routes={props.routes} /></div>);
}
所以我只将其命名为“ NavMenuContainer”吗? 在我看来,这很奇怪。 我应该改为命名组件NavMenuComponent吗? 两者都对我来说很奇怪。 在这种情况下,该组件仅调用connect以从该状态获取1字段。 像这样直接插入连接真的真的很糟糕吗?
export default const NavMenu = connect(state => ({currentPath:state.routing.path})(React.createClas({...}));
好奇地听到您的想法@gaearon关于何时(如果有的话)只是内联connect调用是“好的” ...
怎么称呼它都没关系。 为什么不称之为NavMenu
呢?
我不了解有关内联的问题。
如果您提出了两种比较的方法,将会有所帮助。
好的,例如。
选项1(2个单独的文件,1个用于容器,1个用于组件)
容器/导航菜单
import {connect} from 'react-redux';
import NavMenu from '../components/NavMenu';
export default connect(state => ({currentPath:state.routing.path})(NavMenu);
选项2(1个包含两个文件的单个文件):
组件/导航菜单
import {connect} from 'react-redux';
export default connect(state => ({currentPath:state.routing.path})(React.createClass({
render() {
return <div>the menu {this.props.currentPath} goes here</div>;
}
});
内联的意思是只有一个文件(而不是2个文件),它既是容器又是组件,因为从状态只需要currentPath的一点点。 这只是应该避免的事情,还是在像这样的简单情况下这样做是合理的?
当然,在简单的情况下这样做是合理的。
嗯不错。 您什么时候画线并说“好吧,我会将其移动到2个单独的文件中”?
当组件开始将数据关注点(如何检索/计算数据,如何调度动作)与表示(外观)混合在一起时。 当难以在不同上下文中进行测试或重用时。
@benmonro还有一个想法。 如果要构建组件模块,则有时需要将这些组件连接到应用程序状态树的不同部分,或分别使用各种状态测试它们的表示。 在这种情况下,在此组件模块中包含connect
将限制此功能。
谢谢@gaearon
@sompylasar非常好一点! 谢谢
嗯,经过我的代码以将其重构以将其拆分后,我现在有一个后续问题。 假设我还有一个<NavMenuItem>
容器。 <NavMenu>
组件需要引用<NavMenuItem />
作为子元素。 我应该只在NavMenu组件中执行import NavMenuItem from '../containers/NavMenuItem'
吗? 这如何影响@sompylasar的可测试性?
我会将NavMenuItem做成一个纯粹的演示组件,并将所有需要的数据通过NavMenu通过props传递给它。 这将允许对其进行单独测试。 因此,您将有两个表示形式的(NavMenu,NavMenuItem)和一个已连接的(connect(...)(NavMenuItem))组件。
在此讨论中我缺少的一件事:将它们放在一个文件中时,不能使用浅层渲染进行测试。 我见过人们公开展示性组件和容器组件来解决此问题,然后分别进行测试,因此在这种情况下,我宁愿有两个文件来明确表明这是两件事。 这也突出了这里关注点的分离以及表示组件是独立且可重用的事实。
FWIW我更新了Presentational and Container Components文章以反映我当前的想法。
我们不再鼓励在更新的文档中创建容器组件。
http://redux.js.org/docs/basics/UsageWithReact.html
在Redux文档的实际示例中, @ gaearon似乎组件可以将容器作为子容器。
我错了吗? 这如何影响在其内部呈现智能组件的哑巴组件的可测试性?
但是我找不到让组件成为层次结构中最新组件的方法...
我有一个呈现简单数据列表组件(Dumb)的应用程序。
里面的每件商品都必须连接到商店,因此这是一个明智的选择。
根据文档可以,但是会带来一些问题吗?
感谢!
这如何影响在其内部呈现智能组件的哑巴组件的可测试性?
这确实使测试更难设置(您还需要初始化存储)。 如果不方便,请提取更多接受children
演示性组件,以便您可以在其中传递容器组件。 通常,划分取决于您,您需要评估权衡(易于编写,重构,测试等),并选择如何为您自己分离组件。
好的,所以没有正确的方法。 我们必须逐案评估。许多
谢谢!!
Illunedì2016年2月8日,Dan Abramov [email protected] ha
字幕:
这如何影响在其内部渲染的哑巴组件的可测试性
一个聪明的人?这确实使测试更难设置(您需要初始化
商店)。 如果这不方便,请提取更多
可以接受孩子的演示组件,因此您可以传递容器
内部组件。 通常,划分取决于您,而您需要
评估权衡(易于编写,重构,测试等),并
选择如何自行分离组件。-
直接回复此电子邮件或在GitHub上查看
https://github.com/rackt/redux/issues/756#issuecomment -181143304。
卢卡·科洛内洛(Luca Colonnello)
+39 345 8948718
卢卡 [email protected]
那“布局组件”呢? 我的意思是,当您有多个容器需要放在一个组件中才能将其传递到路由器/导航器时,此包装组件将成为“笨拙的容器的外观容器”,对吗?
@ Emilios1995我有同样的问题...
我有一个在布局组件内使用的Page组件。
此Layout组件具有菜单,页眉和页脚。.内容是Page传递给Layout。的子级。
菜单和标题都是容器!! 因此Layout可能是一个容器,但未连接到商店。
但是,如果我尝试传递菜单和标题的布局,则会有一个页面(容器)呈现布局(组件)并将菜单和标题(容器)传递给它。
这样做的层次结构是正确的,但是我必须在每页中重复菜单和标题,对我来说,每页中的菜单位都是相同的。
@LucaColonnello如果没有代码,我不太了解这个问题。 我是否可以要求您创建一个StackOverflow问题,并提供一个简单的示例来说明您的问题? 我很乐意看看。
尽快
Il sabato 2016年2月27日,Dan Abramov [email protected] ha
字幕:
@LucaColonnello https://github.com/LucaColonnello我不太清楚
无需代码即可了解问题。 我可以请您创建一个
有一个简单的示例来说明您的问题的StackOverflow问题? ID
很高兴看看。-
直接回复此电子邮件或在GitHub上查看
https://github.com/reactjs/redux/issues/756#issuecomment -189672067。
卢卡·科洛内洛(Luca Colonnello)
+39 345 8948718
卢卡 [email protected]
@gaearon这不是问题,我认为这是我在这里提出的一个问题: http : //stackoverflow.com/questions/35729025/should-the-route-handlers-use-containers-or-presentational-components ? noredirect = 1#comment59133192_35729025
我认为丹在媒体上的文章
https://medium.com/@dan_abramov/smart -and-dumb-components-7ca2f9a7c7d0#.3y00gw1mq
澄清所有问题...
2016-03-01 18:19 GMT + 01:00 Emilio Srougo [email protected] :
@gaearon https://github.com/gaearon这不是问题,我认为是
而是我在这里提出的一个问题:
http://stackoverflow.com/questions/35729025/should-the-route-handlers-use-containers-or-presentational-components?noredirect=1#comment59133192_35729025-
直接回复此电子邮件或在GitHub上查看
https://github.com/reactjs/redux/issues/756#issuecomment -190820426。
卢卡·科洛内洛(Luca Colonnello)
+39 345 8948718
卢卡 [email protected]
@gaearon我想知道,如果演示性组件内部可能包含容器组件,那么如何重用呢?
供参考:
我想知道,如果表示性组件可能在内部包含容器组件,那么该组件如何可重用?
如果它的所有使用场景都在其中包含一个特定的容器,那么我看不到有什么不可重用的。 如果不是,则使其接受this.props.children
并创建其他传递内部特定容器或呈现组件的呈现组件。
@gaearon [根组件]容器组件在React-Router上吗?
<Route path="/" component={Root}>
<IndexRoute component={Main} />
<Route path="/account/signIn" component={SignIn} />
</Route>
export default class Root extends React.Component {
render() {
return (
<div>
<div id="container" className="container">
{this.props.children}
</div>
</div>
);
}
谢谢。
上面的@gaearon表示,您更喜欢连接组件以便获得对分发的访问权限(而不是将其从父组件向下传递)。
如果连接这些组件,并且它们也具有从父级当前正在传递的reducer _could_映射的props,您是否可以将它们重构为connect
? 或使用ownProps
来保持它们通过父母。
两种选择之间在功能/性能上有区别吗?
我没有处理大型Redux项目的丰富经验,但是我一直在研究和思考很多有关优化react / redux文件结构的知识。 让我知道这是否有意义:
src/
components/
header/
navigation.js # nav menu list
index.js # Header component
modules/
header/
actions.js # header actions (sticky scroll, collapse mobile menu, etc...)
reducer.js # header actions reducer (export to modules index)
index.js # HeaderContainer (connect to Header component)
index.js # combineReducers and export default configureStore (and middleware)
另一个概念:
src/
components/
navigation.js
logo.js
link.js
list.js
item.js
modules/
header/
actions.js # header actions
wrapper.js # header class (smart) component - to wrap header with functionality (was previously classified as container)
container.js # header functional (dumb) component - to contain universal components
index.js # header actions reducer - to export into modules rootReducer
还是将组件,容器和redux模块分开(即使它们共享相同的名称)会更好吗? 感谢您的任何投入。
我曾经在企业级React-redux项目中工作过,根据我的经验,我可以说一件事,那就是它完全取决于您如何定义项目的体系结构。 我不遵循任何基于容器/组件的体系结构变体,因为就React的目的是使基于UI的组件可重用的意义而言,它们是不切实际的。
因此,我想出了一种基于模块将整个项目分组的简单方法,到目前为止,在可伸缩性,可维护性和代码可读性方面,它一直非常有效。
对我来说,容器和智能组件是完全相同的。 简单的定义是:
容器/智能组件是一个包含JSX标记+事件处理程序+ api调用+ redux的connect / MSTP / MSDP的组件。
愚蠢的组件是纯粹的呈现功能组件。
最有用的评论
我称
components
封装的React组件仅由props驱动,不与Redux对话。 与“哑部件”相同。 无论您的路由器,数据提取库等如何,它们都应保持不变。我将知道Redux,Router等的
containers
React组件称为。它们与应用程序的耦合程度更高。 与“智能组件”相同。