如何管理子组件之间的 UI 状态?

How do I manage UI state among subcomponents?

我想显示带有 React 的可选对象列表。我有列表组件,它拥有多个组,这些组拥有多个元素。显然,列表数据本身应该进入存储,但是 UI 状态(例如,选择了哪个元素)呢?它表面上是根列表元素的 属性,但要将它传递给元素,链上的每个元素(好吧,在这种情况下只有 1 - 组)需要传递它,即使它们不不在乎它。如果我想添加另一个 属性,我需要相当冗长地将它传递到链中。

还有,封装。如何制作一个可能使用不同数据多次实例化的组件?

在其他语言中,我只是将列表指针向下传递到链中,这样子元素就可以从中得到任何他们想要的东西,所以一切都保持封装状态。通量方式是将商店传递给子元素吗? UI 状态与持久数据有单独的存储吗?

让我们从下往上设计它。所以,在底部我们有一个 ListItem。

列表项需要什么才能呈现?假设它是一个标题,一个 body,以及它是否被选中。

还有哪些事件使列表项有意义?可能只是点击。

它有任何状态吗?不,除非我们需要处理诸如悬停之类的事情,或者 ListItem 特定的其他状态,但与其 parent.

无关
var ListItem = React.createClass({
  render(){
    var classNames = ["list-item"];

    if (this.props.selected) classNames.push("selected");

    return (
      <li className={classNames.join} onClick={this.props.onClick}>
        <div className="title">{this.props.title}</div>
        <div className="body">{this.props.body}</div>
      </li>
    );
  }
});

ListGroup 不太明显。

对于 props,我们将要求提供项目列表、所选索引(如果有)和 onSelectionChange 回调,该回调通过 (nextIndex, lastIndex).

调用

这个组件也没有状态。为了加分,我们将允许指定自定义渲染器,使其更易于重用。您可以传递 renderer={component},其中 component 是实现上述 ListItem 接口的东西。

var ListGroup = React.createClass({
  getDefaultProps: function(){
    return {renderer: ListItem, onSelectionChange: function(){}}
  },
  render(){
    return (
      <div>{this.props.items.map(this.renderItem)}</div>
    );  
  },
  renderItem(data, index){
    var selectedIndex = this.props.selectedIndex;
    return (
      <this.props.renderer 
        selected={selectedIndex === index}
        key={i}
        onClick={() => this.props.onSelectionChange(index, selectedIndex)}
        {...data} />
  }
});

然后我们可以像这样渲染 ListGroup:

<ListGroup 
  items={[{title: "foo", body: "bar"}, {title: "baz", body: "quux"]}
  selectedIndex={this.state.i}
  onSelectionChange={(index) => this.setState({i: index})} />

数据是沿着树向下传递的,但只是渲染每个组件所需的数据的本质。 ListItem 不需要完整的项目列表,也不需要选定的索引。它不关心是否允许多项选择,或者只允许一个。它所知道的是当 this.props.selected 为真时它被选中,否则它不是。