如何在React中轻松设置兄弟组件的状态?

时间:2015-06-15 13:03:10

标签: reactjs siblings

我已经开始使用可点击列表组件来驱动select元素。从下面onClick的{​​{1}}可以看出,我将子元素(在本例中为ListItem)的状态传递给父元素({{1} }和ListItem组件)。这工作正常。但是,我还想做的是更改兄弟组件(其他ListItems)的状态,以便在单击其中一个ListItem时可以切换其选定状态。

目前,我只是使用SelectableList来抓取元素,并在未与所点击的CustomSelect的索引匹配时将其更改为已选中。这在某种程度上有效。但是,在几次单击之后,组件的状态尚未由React更新(仅由客户端JS),并且事情开始崩溃。我想要做的是更改兄弟列表项的document.querySelectorAll('ul.cs-select li),并使用此状态刷新SelectableList组件。任何人都可以提供一个比我下面写的更好的替代方案吗?

ListItem

3 个答案:

答案 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道具中的状态传递给每个孩子来重新渲染子项

你可以在这里看到一个有效的例子:

https://jsfiddle.net/fth25erj/

答案 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>
        );
    }
}