反应组件是否应了解其父组件的状态

时间:2016-07-12 14:46:12

标签: checkbox reactjs components

所以我写了这个表行组件,它带有复选框作为其他表组件使用的第一列。以下是组件的简化版本

表格行:

import React, { Component, PropTypes } from 'react'
import { Checkbox } from 'react-bootstrap'

export default class TableRow extends Component {
  constructor(props) {
    super(props)
    this.handleClickCheckbox = this.handleClickCheckbox.bind(this)
  }

  handleClickCheckbox() {
    this.props.onClickCheckbox(this.props.entity.id)
  }

  render() {
    const { checked, children } = this.props 

    return (
      <tr>
        <Checkbox
          checked={checked}
          onChange={this.handleClickCheckbox}
        >&nbsp;</Checkbox>
        {children}
      </tr>
    }

}
TableRow.propTypes = {
  entity: PropTypes.shape({
    id: PropTypes.number
  }),
  checked: PropTypes.bool,
  onClickCheckbox: PropTypes.func
}

表:

import React, { Component, PropTypes } from 'react'
import { Table } from 'react-bootstrap'
import TableRow from './TableRow'

class SampleTable extends Component {
  constructor(props) {
    super(props)
    this.state = {
      checkedEntitiesIds: []
    }
    this.handleChangeChecked = this.handleChangeChecked.bind(this)
  }

  handleChangeChecked(id) {
    const { checkedEntitiesIds } = this.state
    const indexOfEntity = checkedEntitiesIds.indexOf(id)
    // uncheck it if it's already checked, vice versa
    this.setState({
      checkedEntitiesIds : indexOfEntity > -1 ?
      checkedEntitiesIds.filter((id, index) => index != indexOfEntity) :
      [...checkedEntitiesIds, id]
    })
  }

  render() {
    const { entities } = this.props // can come from either parent component or subcribe to redux state
    return (
      <Table>
        <thead>
          <tr>
            <th>placeholder for checkbox column</th>
            <th>dummy header 1</th>
            <th>dummy header 2</th>
          </tr>
        </thead>
        <tbody>
        {entities.map(entity => 
          <TableRow
            key={entity.id}
            entity={entity}
            checked={this.state.checkedIds.indexOf(action.id) > -1}
            onClickCheckbox={this.handleChangeChecked}
          >
            <td>dummy table cell 1</td>
            <td>dummy table cell 2</td>
          </TableRow>
        )}
        </tbody>
      </Table>
    )
  }
}

我使复选框成为受控输入,因为checked props的值可能来自数据库。每个表行都有一个实体道具,可以唯一地标识该行。我在表组件中维护checkedEntitiesIds状态,而不是在TableRow组件中维护已检查状态,因为我觉得它为我提供了最大的灵活性。

我的一个同事并不喜欢他必须在每个表组件中维护checkedEntitiesIds状态以及handleChangeChecked函数这一事实。他建议将checkedEntitiesIds状态添加到TableRow中,并将handleChangeChecked的函数体移动到TableRow。它的工作方式是每次单击/更改复选框时,TableRow中的checkedEntitiesIds状态将首先更新,然后传递回父表,因此父组件的checkedEntitiesIds状态也会更新。这样他就不需要一遍又一遍地编写检查/取消检查逻辑。

有些事情让我无法做出这样的改变:

  1. 让TableRow知道checkedEntitiesIds,它应该是Table的状态,这是违反直觉的。
  2. 重复的事实来源。
  3. TableRow将具有状态。如果我在这里错了,请纠正我。我的理解是那些低级哑组件应该很少有自己的状态。
  4. 在某些scanerios中,复选框点击事件可能不一定会立即导致状态更改。例如,单击复选框时首先提示确认模式,仅在选择yes时更新状态。使用TableRow中的状态并不能提供这种灵活性。
  5. 我几乎可以肯定他的建议有些反模式。我只是不确定我放置组件的方式是否合理以及我列出的那些点是否有效。我在这里错过任何重要的东西来说服他吗?有没有办法在可重用性方面改进我的代码。任何见解都将不胜感激。

1 个答案:

答案 0 :(得分:2)

你提出的所有观点似乎都是合法的。 Smart vs Dumb Component模式似乎是每个人都采用的模式(并且有充分的理由,当没有明显的分离时,代码会变得非常混乱)。

你的同事建议另一个根本错误的事实是,从孩子到父母的数据向上流动。 React的核心思想之一是单向数据流。要传播到父级的子级的更改应该通过商店(或任何数据层)来维护此单向属性。

React Data Flow

让孩子通知更新的父级,它会增加一层并发症(如果它触发了一个孩子的更新,然后回到父级并永远循环?),React试图摆脱它第一个地方(正是这个问题让人们怀疑Angular应用程序的可扩展性,使它们在更大的范围内变得过于复杂)。