在reactjs中引用孩子的正确方法

时间:2014-03-06 11:36:26

标签: javascript reactjs

我正在调查facebook的react.js,发现它到目前为止非常酷。我试图创建一个简单的文件夹结构,您可以在其中打开和关闭每个文件夹。 我的结构看起来像这样

  

<Folder>   <Header/>   <Content/>   </Folder>

单击标题会导致文件夹隐藏/显示其内容。这很容易通过国家完成。

但现在我想拥有多个文件夹和一个“全部切换”按钮。如何让按钮切换所有孩子而不会造成大杂乱?我使用refs来解决它们,但我认为这是一种不好的做法,因为the Documentation状态:

  

...你的第一个倾向通常是试图在你的应用程序中使用refs“让事情发生”......

     

...考虑组件层次结构中应该拥有哪个状态。通常,很明显,“拥有”该状态的适当位置在层次结构中处于更高级别。

我创建了一个Fiddle来演示整个事情。它工作正常,但我认为这不是一个很好的解决方案。

PS(奖金问题): 是不是通过不渲染来隐藏内容(比如在小提琴中完成),或者只是添加'display:none;' styletag?

1 个答案:

答案 0 :(得分:2)

在reactjs中引用儿童的正确方法 - &gt;不要引用儿童来查询他们的状态。如果您需要从父级查询该状态,请将该状态放在父级中,并将状态作为道具注入子级中。

以下是您的代码重做: http://jsfiddle.net/t5fwn/7/

/** @jsx React.DOM */
var initialState = {
    "folders": [
    {
        "name": "folder1",
            "open": false,
            "files": [{
            "text": "content1 1"
        }]
    }, 
    {
        "name": "folder2",
            "open": false,
            "files": [{
            "text": "content2 1"
        }, {
            "text": "content2 2"
        }]
    }
    ]
};

var Folder = React.createClass({

    render: function() {
        var items = [];
        if( this.props.folderData.open ){
            this.props.folderData.files.forEach(function(file) {
                items.push( <div className="itemBox">{file.text}</div>);
            });
        }
        return (
            <div className="items_directory">
                <div className="folder_header" onClick={this.props.onFolderClick}>{this.props.folderData.name}</div>
                <div className="folder_content">
                    {items}
                </div>
            </div>
        );
    }
});

var FoldersManager = React.createClass({

    getInitialState: function() {
        return this.props.initialState;
    },

    render: function(){
        var self = this;
        var folderComponents = this.state.folders.map(function(folder,folderIndex) {
          var onFolderClick = function() {
              self.toggleFolder(folderIndex);
          };
          return <Folder folderData={folder} onFolderClick={onFolderClick}/>;
        });
        return (
            <div>
                <button onClick={this.toggleAll}>Toggle All</button>
                <div>
                    {folderComponents}
                </div>
            </div>
        );
    },

    toggleFolder: function(folderIndex) {
            var newState = this.state;
            newState.folders[folderIndex].open = !newState.folders[folderIndex].open;
            this.setState(newState);
    },

    toggleAll: function(){
        var newState = this.state;
        var newOpenToSet = this.isAllFoldersOpen() ? false : true;
        newState.folders.forEach(function(folder) {
          folder.open = newOpenToSet;
        });
        this.setState(newState);
    },

    isAllFoldersOpen: function() {
      return this.countFoldersOpen() == this.state.folders.length;
    },

    countFoldersOpen: function() {
      var i = 0;
      this.state.folders.forEach(function(folder) {
        if ( folder.open ) i++;
      });
      return i;
    }

});

React.renderComponent(<FoldersManager initialState={initialState} />, document.body);

最好有一种管理所有文件夹状态打开/关闭的管理器组件。 Folder组件可以简单地用于呈现文件夹,但不能用于管理文件夹的状态,或者在全局toggleAll操作中,您必须“查询”子组件以了解其状态。


PS(奖金问题):我认为不呈现未显示的内容会更优雅。但是出于性能原因,如果隐藏/显示状态经常变化,最好使用display : none;:它会产生相同的视觉效果,但DOM差异会更小。除非您在未呈现隐藏元素的情况下发现性能问题,否则不要对此进行优化。