これをボトムアップで設計しましょう。そのため、下部に ListItem があります。
リスト項目をレンダリングするために何が必要ですか? タイトル、本文、および選択されているかどうかを考えてみましょう。
また、リスト項目を意味するイベントは何ですか? おそらくonClickだけです。
状態はありますか?いいえ、ホバーされている、または ListItem に固有のその他の状態などを処理する必要がある場合を除きますが、その親には関係ありません。
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}
上記の 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 は、項目の完全なリストも、選択されたインデックスも必要としません。複数の選択が許可されているか、1 つだけが許可されているかは問題ではありません。それが知っているのは、this.props.selected
が真実である場合に選択され、それ以外の場合は選択されないということだけです。