我已经有了可点击列表组件的开始,它将用来驱动一个select元素。正如您从下面看到onClick的ListItem,我正在将子元素的状态(ListItem在这种情况下)传递给父元素(SelectableList和CustomSelect)。一切正常。但是,我还想做的是更改 同级 组件(其他ListItems)的状态,以便在单击ListItems之一时可以切换其选定状态。
onClick
ListItem
SelectableList
CustomSelect
此刻,我只是document.querySelectorAll('ul.cs-select li)用来获取元素,并在与clicked的索引不匹配时将类更改为selected ListItem。这在一定程度上起作用。但是,单击几次后,React尚未更新组件的状态(仅通过客户端JS),并且一切开始崩溃。我想做的是更改this.state.isSelected同级列表项的,并使用此状态刷新SelectableList组件。有人能提供我下面编写的更好的替代方法吗?
document.querySelectorAll('ul.cs-select li)
this.state.isSelected
var React = require('react'); var SelectBox = require('./select-box'); var ListItem = React.createClass({ getInitialState: function() { return { isSelected: false }; }, toggleSelected: function () { if (this.state.isSelected == true) { this.setState({ isSelected: false }) } else { this.setState({ isSelected: true }) } }, handleClick: function(listItem) { this.toggleSelected(); this.props.onListItemChange(listItem.props.value); var unboundForEach = Array.prototype.forEach, forEach = Function.prototype.call.bind(unboundForEach); forEach(document.querySelectorAll('ul.cs-select li'), function (el) { // below is trying to // make sure that when a user clicks on a list // item in the SelectableList, then all the *other* // list items get class="selected" removed. // this works for the first time that you move through the // list clicking the other items, but then, on the second // pass through, starts to fail, requiring *two clicks* before the // list item is selected again. // maybe there's a better more "reactive" method of doing this? if (el.dataset.index != listItem.props.index && el.classList.contains('selected') ) { el.classList.remove('selected'); } }); }, render: function() { return ( <li ref={"listSel"+this.props.key} data-value={this.props.value} data-index={this.props.index} className={this.state.isSelected == true ? 'selected' : '' } onClick={this.handleClick.bind(null, this)}> {this.props.content} </li> ); } }); var SelectableList = React.createClass({ render: function() { var listItems = this.props.options.map(function(opt, index) { return <ListItem key={index} index={index} value={opt.value} content={opt.label} onListItemChange={this.props.onListItemChange.bind(null, index)} />; }, this); return <ul className="cs-select">{ listItems }</ul>; } }) var CustomSelect = React.createClass({ getInitialState: function () { return { selectedOption: '' } }, handleListItemChange: function(listIndex, listItem) { this.setState({ selectedOption: listItem.props.value }) }, render: function () { var options = [{value:"One", label: "One"},{value:"Two", label: "Two"},{value:"Three", label: "Three"}]; return ( <div className="group"> <div className="cs-select"> <SelectableList options={options} onListItemChange={this.handleListItemChange} /> <SelectBox className="cs-select" initialValue={this.state.selectedOption} fieldName="custom-select" options={options}/> </div> </div> ) } }) module.exports = CustomSelect;
父组件应将回调传递给子组件,并且每个子组件的状态更改时都会触发该回调。实际上,您可以将所有状态保留在父级中,将其用作单个事实点,然后将“ selected”值作为道具传递给每个孩子。
在这种情况下,孩子可能看起来像这样:
var Child = React.createClass({ onToggle: function() { this.props.onToggle(this.props.id, !this.props.selected); }, render: function() { return <button onClick={this.onToggle}>Toggle {this.props.label} - {this.props.selected ? 'Selected!' : ''}!</button>; } });
它没有状态,仅onToggle在单击时触发回调。父母看起来像这样:
onToggle
var Parent = React.createClass({ getInitialState: function() { return { selections: [] }; }, onChildToggle: function(id, selected) { var selections = this.state.selections; selections[id] = selected; this.setState({ selections: selections }); }, buildChildren: function(dataItem) { return <Child id={dataItem.id} label={dataItem.label} selected={this.state.selections[dataItem.id]} onToggle={this.onChildToggle} /> }, render: function() { return <div>{this.props.data.map(this.buildChildren)}</div> } });
它持有一系列状态选择,当它处理子项的回调时,它setState通过将其状态在selectedprop中传递给每个子项来重新渲染子项。
setState
selected
您可以在这里看到一个有效的示例:
https://jsfiddle.net/fth25erj/