React生命周期方法应该在容器组件或表示组件中实现吗?

时间:2016-05-10 14:49:19

标签: reactjs redux flux

我试图在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;
    }

    [...]
}

一方面,这似乎是一个表现性问题,因为它控制组件何时应该重新渲染。然而,另一方面,当用户点击新标签,通过动作更新应用程序的状态时,这是一种处理方式,因此我将其归类为行为。

3 个答案:

答案 0 :(得分:4)

我认为这是一个意见问题。我个人希望尽可能地保持我的表现成分。这使我也可以将我的大多数表示组件写为stateless functions,这些组件在React更新中越来越多地被优化。这意味着如果我能帮助它,我将阻止任何表示组件具有内部状态。

就你的例子而言,我不相信这是一个表达问题,因为componentShouldUpdate是道具的纯函数,每当使用这个组件时都应该传递。即使这个组件更新了应用程序的状态,我相信因为它没有内部状态,所以它不一定是行为。

同样,我不认为在这里做事有正确或错误的方式。它让我想起关于whether or not Redux should handle all application state的讨论。我认为,如果你保持将演示组件尽可能简单(可重复使用)的想法,你可以找出在任何情况下放置生命周期方法的正确位置。

答案 1 :(得分:4)

应尽可能靠近树的根控制数据。这样做可以提供一些简单的优化,因为你只需要传递你需要的东西。

这将降低到您控制某些生命周期组件的位置。正如mgmcdermott所提到的,许多生命周期组件实际上取决于您正在做什么,但最好的情况是拥有最简单,最笨的组件。

在我的大多数项目中,在我的react目录中,我有components/views/。我始终倾向于视图应该尽可能多地执行繁重的工作。话虽如此,我已经构建了许多使用生命周期方法的组件,例如componentDidMountcomponentWillMountcomponentWillUnmount,但我通常尝试在我的视图中隔离更新,因为我认为他们的一项工作是控制数据流。这意味着componentShouldUpdate会住在那里。就个人而言,我认为componentShouldUpdate纯粹是最终的优化,我只会在重新渲染期间遇到大量性能问题的情况下使用它。

我不能确定我理解你的继承自React.Component"题。如果您要问是否使用纯函数,es6 classReact.createClass,我不知道有一个标准规则,但保持一致是好的

要解决您是否正在处理行为或演示文稿,行为是单击,但重新绘制是演示文稿。您的Tab组件中可能存在您的行为,您可以在Tabs视图中重新绘制。 Tabs视图从redux传递您的方法,将当前活动的标签设置为您的各个Tab组件,然后可以通过redux发送标签切换的行为,以便您可以进行演示componentShouldUpdate。这有意义吗?

因此,您容器中的mapToDispatch方法将具有设置活动标签的功能,让我们将其称为activateTab(idx),该标签采用基于0的标签索引。您的容器将其传递给您控制的包含组件views/Tabs,并将该方法传递给components/Tabcomponents/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)

你的问题不太正确。

使用生命周期方法的简单操作不会将组件定义为表示容器组件。

生命周期方法就是这样 - 为了方便起见,你可以做任何你想做的事情。

容器组件 通常进行一些设置,将自身连接到这些生命周期方法中的应用程序数据流。这就是使它成为容器组件的原因,而不是它使用某些生命周期方法的事实。

演示组件 通常哑和无状态,因此它们通常不需要连接这些生命周期方法。这并不意味着情况总是如此。表示组件可以是有状态的(尽管这通常是不期望的)并且无状态组件可以使用生命周期方法,但是以与容器组件完全不同的方式。它可能会向文档添加一些事件侦听器,或者以完全无状态的方式调整输入的光标位置。

也许你正在混淆容器组件和有状态组件。这些是不同的东西,虽然容器组件是有状态的,但有状态组件不一定充当容器组件。