子组件在ReactJS中是否应该具有自己的状态?

时间:2016-01-22 09:40:46

标签: javascript reactjs

我在ReactJS中开发了一个具有特定外观的对话框。该对话框应该为用户提供选择开始时间和结束时间的可能性(类似于当员工暂停并回来,会议等时的活动)。除了选择那两次之外,对话框还应该给出一个文本框来写一个关于活动的评论。

我的想法是在' ActivityDialog'中创建一个可重用的组件。零件。由于我需要选择两次(开始和结束时间),我决定制作一个名为“TimePicker”的可重复使用的组件。并使用它两次。根据规格,我需要分别用箭头向上/向下箭头选择(值改变)时间(2小时和2分钟)中的每个4位数。因此,我决定制作另一个可重复使用的组件,我命名为DigitPicker'。

  1. 所以,我的问题是,如果TimePicker和DigitPicker有自己的状态(时间和数字),或者状态应该是最老的'父组件(ActivityDialog)?
  2. 在任何一种情况下,我认为计算具有数十/单位变化的新时间的逻辑应该在TimePicker组件中,而不是在ActivityDialog组件中。

  3. 如果第一个问题的答案是我只需要最老的'父组件,然后我的TimePicker和DigitPicker渲染方法将在HTML中包含 this.props.something 。然后我对这两个组件可重用的想法就崩溃了。为什么? 好吧,让我们来看看TimePicker-DigitPicker连接。从TimePicker,我将传递一个数字和回调,它将改变数字道具的值,这将触发重新渲染,并显示新的数字。但是当别人拿走我的DigitPicker组件时,当我触发他传给我的回调时,他可能不会更改数字道具。如果DigitPicker有自己的状态,那就可以解决。

  4. 所以我的问题更多的是关于ReactJS的概念,我应该如何使用父子连接,如果孩子有自己的状态或依赖父父发送的道具?这又如何影响我希望所有树组件保持可重用的事实?

    非常感谢任何建议。

3 个答案:

答案 0 :(得分:2)

为了呈现相反的论点,如果你在React中最小化组件状态的使用,它通常更灵活,更容易理解 - 它允许你使用纯JS来管理React之外的状态(例如使用Flux或类似MVC系统骨干)。在您的情况下,您的父母可能需要在任何情况下跟踪和处理每个“时间”更改 - 验证开始时间< =结束时间等,以便它可以突出显示为红色,例如,何时超出范围或等等

但是,您可能希望将您的API保持在您的TimePicker简单 - 一个包含Time对象的简单'值'支柱(或KISS - 只是一个数字,自午夜以来的秒数等),以及一个onValueChanged回调,它是当用户要求更改它时,传回新值。该组件使用该值来计算要呈现的数字,每个数字和一个回调到每个DigitPicker。如果在更改回调之后,父级没有传回更新的值,那么可能他们不希望数字更改产生任何影响(可能是因为您已达到父级希望您的最小/最大时间使用)。如果他们确实想要改变价值,那么他们需要传递新价值。

归结为您是要模拟“受控”组件还是“非受控”组件:请参阅https://facebook.github.io/react/docs/forms.html以获取解释。在我的大型应用中,我没有不受控制的应用......

重用是相同的两种方式,但如果您指定组件的值而不是让它控制其值,则可以获得更多控制权。

答案 1 :(得分:0)

你或多或少地回答了你自己的问题;拥有可重复使用的组件,让它们保持自己的状态要好得多,然后根据其他组件将它们提供给它们。

关注点和所有问题的分离,我可以想象你的DigitPicker组件在TimePickers和ActivityDialogs的上下文之外是有用的。

然而,为了实际让父母回应孩子的变化,我倾向于通过其道具为孩子提供一个eventHandler(绑定到父母)。 因此,当您选择一个数字时,会触发生活在TimePicker中的事件处理程序,这反过来可能会触发ActivityDialog中的事件处理程序。

但是,国家生活在孩子们身上。

编辑:我花时间实际构建这些组件,对我来说更容易理解它

class ActivityDialog extends React.Component {
    render(){
        return (
            <div>
                <TimePicker></TimePicker>
                <TimePicker></TimePicker>
            </div>)
  }

}

class TimePicker extends React.Component {
    constructor() {
    super();
    this.state = {
      hours:0,
      minutes:0
    }
    this.onTimeChange = this.onTimeChange.bind(this);
  }

  onTimeChange(type, value) {
    if (type == 'hours') {
       this.setState({hours:value});
    }
    if (type == 'minutes') {
       this.setState({minutes:value});
    }
  }

  render() {
    return(
        <div> {this.state.hours} : {this.state.minutes}
            <div className="numberPicker">
            <NumberPicker handler={this.onTimeChange} type="hours" max={24}></NumberPicker>
            <NumberPicker handler={this.onTimeChange} type="minutes" max={60}></NumberPicker>
        </div>
      </div>
      );
  }
}

class NumberPicker extends React.Component {
    constructor() {
    super();
    this.onNumberChange = this.onNumberChange.bind(this);
  }

  onNumberChange(e){
    this.props.handler(this.props.type, e.target.textContent);
  }

  render(){
        var N = this.props.max; 
        var numbers = Array.apply(null, {length: N}).map(Number.call, Number)
        return <div className="number-container" > {
            numbers.map((number)=>{
                return(<div onClick={this.onNumberChange} key={number} className="number">{number}</div>);
            })
          }</div>
      }

}

结论:在这种情况下,您只需要将'digits'保持为TimePicker的状态。

这里是jsfiddle https://jsfiddle.net/nikkikoole/69z2wepo/28489/

答案 2 :(得分:0)

在可重用组件中有一个状态是完全有效的,特别是如果它保持用户输入。您仍然可以在props中为其提供回调,根据您的需要响应onChange,onBlur或其他事件,或者您可以通过refs与可重用组件进行通信,这是获取用户输入的相当灵活的方式。

重要的是,可重用组件应尽可能独立于其环境。如果你没有为它提供状态,那么组件(在你的Pickers的情况下)将依赖于从其父节点传递的props,表示它在每个rerender上的当前用户输入以及在组件层次结构中更高的当前输入的回调在每一个变化中,这并不是你想要的每一个变化,它已经迫使你在你的父组件中进行相对较大的调整,这忽略了可重用性。