我试图在React和Redux中实现container components,并且我不确定应该对生命周期方法负责什么;容器或表示组件。有人可能会说,生命周期方法在控制DOM更新时是表现性的,但在这方面,他们也不是行为吗?
此外,我迄今为止看到的容器组件的所有实现都使用react-redux
绑定,就像我自己一样。即使我将问题清楚地分开,在行为组件的情况下继承React.Component
是否合适?
例如,我工作的应用程序具有Tab
表示组件,并使用shouldComponentUpdate
方法:
class Tabs extends Component {
shouldComponentUpdate(nextProps) {
const { activeTab } = this.props;
return activeTab !== nextProps.activeTab;
}
[...]
}
一方面,这似乎是一个表现性问题,因为它控制组件何时应该重新渲染。然而,另一方面,当用户点击新标签,通过动作更新应用程序的状态时,这是一种处理方式,因此我将其归类为行为。
答案 0 :(得分:4)
我认为这是一个意见问题。我个人希望尽可能地保持我的表现成分。这使我也可以将我的大多数表示组件写为stateless functions,这些组件在React更新中越来越多地被优化。这意味着如果我能帮助它,我将阻止任何表示组件具有内部状态。
就你的例子而言,我不相信这是一个表达问题,因为componentShouldUpdate
是道具的纯函数,每当使用这个组件时都应该传递。即使这个组件更新了应用程序的状态,我相信因为它没有内部状态,所以它不一定是行为。
同样,我不认为在这里做事有正确或错误的方式。它让我想起关于whether or not Redux should handle all application state的讨论。我认为,如果你保持将演示组件尽可能简单(可重复使用)的想法,你可以找出在任何情况下放置生命周期方法的正确位置。
答案 1 :(得分:4)
应尽可能靠近树的根控制数据。这样做可以提供一些简单的优化,因为你只需要传递你需要的东西。
这将降低到您控制某些生命周期组件的位置。正如mgmcdermott所提到的,许多生命周期组件实际上取决于您正在做什么,但最好的情况是拥有最简单,最笨的组件。
在我的大多数项目中,在我的react目录中,我有components/
和views/
。我始终倾向于视图应该尽可能多地执行繁重的工作。话虽如此,我已经构建了许多使用生命周期方法的组件,例如componentDidMount
,componentWillMount
,componentWillUnmount
,但我通常尝试在我的视图中隔离更新,因为我认为他们的一项工作是控制数据流。这意味着componentShouldUpdate
会住在那里。就个人而言,我认为componentShouldUpdate
纯粹是最终的优化,我只会在重新渲染期间遇到大量性能问题的情况下使用它。
我不能确定我理解你的继承自React.Component
"题。如果您要问是否使用纯函数,es6 class
或React.createClass
,我不知道有一个标准规则,但保持一致是好的
要解决您是否正在处理行为或演示文稿,行为是单击,但重新绘制是演示文稿。您的Tab
组件中可能存在您的行为,您可以在Tabs
视图中重新绘制。 Tabs
视图从redux传递您的方法,将当前活动的标签设置为您的各个Tab
组件,然后可以通过redux发送标签切换的行为,以便您可以进行演示componentShouldUpdate
。这有意义吗?
因此,您容器中的mapToDispatch
方法将具有设置活动标签的功能,让我们将其称为activateTab(idx)
,该标签采用基于0的标签索引。您的容器将其传递给您控制的包含组件views/Tabs
,并将该方法传递给components/Tab
。 components/Tab
将有一个onClick
方法正在侦听您的某个DOM元素,然后调用this.props.activateTab(myIndex)
(您也可以将activateTab的绑定版本传递给components/Tab
,以便它不必知道它自己的索引),它会触发redux,然后将你的数据传回views/Tabs
,它可以根据来自redux的数据处理componentShouldUpdate
。
扩展编辑:由于此标记已被接受,因此我会将我的代码示例分解为普通人可用的内容。
暂时不说,我不打算写多少redux,因为这可能非常依赖于应用程序,但我假设您有一个activeTabIdx
挂起父级的状态
<强>容器/ TabExample.jsx 强>
import { connect } from 'react-redux'
import Tabs from 'views/Tabs.js'
const mapStateToProps = function (state) {
return {
activeTabIdx: state.activeTabIdx
// And whatever else you have...
}
}
const mapDispatchToProps = function (dispatch) {
return {
activateTab: function (idx) {
dispatch({
action: 'ACTIVATE_TAB_IDX',
idx: idx
}) // You probably want this in a separate actions/tabs.js file...
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Tabs)
<强>视图/ Tabs.js 强>
import React, { createClass } from 'react'
import Tab from 'components/Tab.js'
const { number, func } = React.PropTypes
// Alternatively, you can use es6 classes...
export default createClass({
propTypes: {
activeTabIdx: number.isRequired,
activateTab: func.isRequired
},
render () {
const { activeTabIdx } = this.props
const tabs = ['Tab 1', 'Tab 2', 'Tab 3']
return (
<div className='view__tabs'>
<ol className='tabs'>
{this.renderTabLinks(tabs, activeTabIdx)}
</ol>
</div>
)
},
renderTabLinks (tabs, activeTabIdx) {
return tabs.map((tab, idx) => {
return (
<Tab
onClick={this.props.activateTabIdx.bind(this, idx)}
isActive={idx === activeTabIdx}
>
{tab}
</Tab>
)
})
}
})
<强>组件/ Tab.js 强>
import React, { createClass } from 'react'
const { func, bool } = React.PropTypes
// Alternatively, you can use es6 classes...
export default createClass({
propTypes: {
children: node.isRequired,
onClick: func.isRequired,
isActive: bool.isRequired
},
handleClick (e) {
const { isActive, onClick } = this.props
e.preventDefault()
if (!isActive) {
onClick()
}
},
render () {
const { children, isActive } = this.props
const tabClass = isActive
? 'tabs__items tabs__items--active'
: 'tabs__items'
return (
<li className={tabClass}>
<a className='tabs__item-link' onClick={this.handleClick}>
{children}
</a>
</li>
)
}
这将主要做正确的事情。请注意,这不会处理/关注标签内容,因此,您可能希望以不同方式构建视图。
答案 2 :(得分:1)
你的问题不太正确。
使用生命周期方法的简单操作不会将组件定义为表示或容器组件。
生命周期方法就是这样 - 为了方便起见,你可以做任何你想做的事情。
容器组件 通常进行一些设置,将自身连接到这些生命周期方法中的应用程序数据流。这就是使它成为容器组件的原因,而不是它使用某些生命周期方法的事实。
演示组件 通常哑和无状态,因此它们通常不需要连接这些生命周期方法。这并不意味着情况总是如此。表示组件可以是有状态的(尽管这通常是不期望的)并且无状态组件可以使用生命周期方法,但是以与容器组件完全不同的方式。它可能会向文档添加一些事件侦听器,或者以完全无状态的方式调整输入的光标位置。
也许你正在混淆容器组件和有状态组件。这些是不同的东西,虽然容器组件是有状态的,但有状态组件不一定充当容器组件。