我已经开始使用可点击列表组件来驱动select元素。从下面onClick
的{{1}}可以看出,我将子元素(在本例中为ListItem
)的状态传递给父元素({{1} }和ListItem
组件)。这工作正常。但是,我还想做的是更改兄弟组件(其他ListItems)的状态,以便在单击其中一个ListItem时可以切换其选定状态。
目前,我只是使用SelectableList
来抓取元素,并在未与所点击的CustomSelect
的索引匹配时将其更改为已选中。这在某种程度上有效。但是,在几次单击之后,组件的状态尚未由React更新(仅由客户端JS),并且事情开始崩溃。我想要做的是更改兄弟列表项的document.querySelectorAll('ul.cs-select li)
,并使用此状态刷新SelectableList组件。任何人都可以提供一个比我下面写的更好的替代方案吗?
ListItem
答案 0 :(得分:23)
父组件应该将回调传递给子组件,并且每个子组件在其状态更改时将触发该回调。您实际上可以将父级中的所有状态保存为单点,并将“选定”值作为道具传递给每个子项。
在这种情况下,孩子可能看起来像这样:
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
回调。父母看起来像这样:
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
通过将selected
道具中的状态传递给每个孩子来重新渲染子项
你可以在这里看到一个有效的例子:
答案 1 :(得分:1)
兄弟姐妹 - 兄弟沟通的另一个策略是使用观察者模式。
观察者模式是一种软件设计模式,其中对象可以向多个其他对象发送消息。
使用此策略不需要兄弟姐妹或亲子关系。
在React的上下文中,这意味着某些组件订阅接收特定消息,而其他组件则向这些订阅者发布消息。
组件通常会在componentDidMount方法中订阅,并在componentWillUnmount方法中取消订阅。
以下是4个实现Observer Pattern的库。它们之间的差异很微妙 - EventEmitter是最受欢迎的。
- PubSubJS:&#34;用JavaScript编写的基于主题的发布/订阅库。&#34;
- EventEmitter:&#34;浏览器的偶数JavaScript。&#34;它实际上是一个库的实现,它已作为nodejs核心的一部分存在,但对于浏览器而言。
- MicroEvent.js:&#34;事件发射器微库 - 20行 - 用于节点和浏览器&#34;
- mobx:&#34;简单,可扩展的状态管理。&#34;
取自:8 no-Flux strategies for React component communication,这也是一般的读物。
答案 2 :(得分:0)
以下代码可帮助我设置两个兄弟之间的通信。设置在render()和componentDidMount()调用期间在其父级中完成。
class App extends React.Component<IAppProps, IAppState> {
private _navigationPanel: NavigationPanel;
private _mapPanel: MapPanel;
constructor() {
super();
this.state = {};
}
// `componentDidMount()` is called by ReactJS after `render()`
componentDidMount() {
// Pass _mapPanel to _navigationPanel
// It will allow _navigationPanel to call _mapPanel directly
this._navigationPanel.setMapPanel(this._mapPanel);
}
render() {
return (
<div id="appDiv" style={divStyle}>
// `ref=` helps to get reference to a child during rendering
<NavigationPanel ref={(child) => { this._navigationPanel = child; }} />
<MapPanel ref={(child) => { this._mapPanel = child; }} />
</div>
);
}
}