react-bootstrap ModalTrigger在卸载父元素时不会隐藏

时间:2015-02-19 16:28:04

标签: react-bootstrap

当使用react-bootstrap的ModalTrigger和一系列项目时,我们遇到了一个奇怪的行为,因为当卸载父/所有者项目时它不会消失。我们怀疑这与React的虚拟DOM和差异机制,和/或我们自己滥用ModalTrigger有关。

设置很简单:内容反应组件具有保存项目名称数组的状态。它还有一个onClick(name)函数,它通过setState从数组中删除该名称。在渲染中,它使用_.map来创建一堆Item反应组件。

每个Item组件显示其名称和一个ModalTrigger,其中包含标有&#34的按钮;删除我"。点击按钮,它打开模态;点击"确定"在模态中,它执行内容删除功能的回调。

删除最后一项时,它可以正常工作:最后的Item组件已卸载,并使用它,ModalTrigger及其相应的模态。

我们看到的问题是删除除最后一项以外的任何项目。该项目被删除但模态保持打开,而我会天真地期望模态消失,因为父ModalTrigger消失了。不仅如此,当点击" ok"再次,列表中的下一个项目被删除,依此类推,直到模态恰好与最后一个项目相关联,此时点击" ok"最后会隐藏它。

我们的集体预感是,这是由overlayMixin的_overlayTarget是文档中的匿名元素引起的,因此不同的ModalTriggers不会区分它们。因此,当父级卸载并且React查找DOM差异时,它会看到之前的触发器并且说“嘿,这可能有效”#。

通过在Item的内部_onClick()函数中添加hide()调用可以很容易地解决整个问题,如代码中注释掉的那样,我们最终得出了我的问题:

我是否正确使用ModalTrigger,因为期望在父代卸载时它会消失?这就是我期望React一般工作的方式,这意味着react-bootstrap中的一个错误。

或者我应该明确地调用hide(),因为他们设计了这个组件的方式吗?

以下是一段重现此内容的代码。

谢谢!

var DeleteModal = React.createClass({
    render:function() {
        return (
            <ReactBootstrap.Modal onRequestHide = {this.props.onRequestHide} title = "delete this?">
                <div className="modal-body">
                    Are you sure?
                </div>
                <div className="modal-footer">
                    <button onClick={this.props.onOkClick}>ok</button>
                    <button onClick={this.props.onRequestHide}>cancel</button>
                </div>
            </ReactBootstrap.Modal>
        );
    }
});

var Item = React.createClass({
    _onClick:function() {
        //this.refs.theTrigger.hide();
        this.props.onClick(this.props.name);
    },
    render:function() {
        return (
            <div>
                <span>{this.props.name}</span>
                <ModalTrigger modal={<DeleteModal onOkClick={this._onClick}/>} ref="theTrigger">
                    <button>delete me!</button>
                </ModalTrigger>
            </div>
        );
    }
});

var Content = React.createClass({
    onClick:function(name) {
        this.setState({items:_.reject(this.state.items, function(item) {return item === name;})});
    },
    getInitialState:function() {
        return {items : ["first", "secondth", "thirdst"]};
    },
    render:function() {
        return (
            <div>
                {_.map(this.state.items, function(item, i) {
                    return (
                        <Item name={item} onClick={this.onClick} key={i}/>
                    )}.bind(this)
                )}
            </div>
        );
    }
});

React.render(<Content/>, document.getElementById("mydiv"));

1 个答案:

答案 0 :(得分:0)

原来这是对React&#34; key&#34;的误用。属性。我们为映射对象提供了整数键,所以当再次调用render时,会给出相同的初始键,这就是为什么React认为它应该重用相同的DOM元素。

如果相反我们给它key = {item}(其中item是一个简单的字符串)它在我们的例子中解决了它;然而,这引入了一个微妙的错误,如果有2个相同的字符串,React将只显示一个。

试图通过给它键来智取它= {item + i}引入了一个更微妙的错误,其中显示了重复的项目,但是大量删除,但在这种情况下,错误是在onClick方法中需要的修改为接受某种索引。

因此,我的要点是密钥必须是唯一的字符串,并且回调应该在执行任何修改时考虑这些密钥。