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

Posted

技术标签:

【中文标题】在 reactjs 中引用子项的正确方法【英文标题】:Correct way to reference children in reactjs 【发布时间】:2014-04-09 00:30:27 【问题描述】:

我正在研究 facebook 的 react.js,发现它到目前为止非常酷。我尝试制作一个简单的文件夹结构,您可以在其中打开和关闭每个文件夹。 我的结构是这样的

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

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

但现在我想要多个文件夹和一个“全部切换”按钮。如何在不造成混乱的情况下获得切换所有孩子的按钮?我使用 refs 来解决它们,但我认为这是一种不好的做法,因为 the Documentation 指出:

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

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

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

PS(奖金问题): 通过不渲染它(就像在小提琴中所做的那样)隐藏内容更好,或者只是添加一个'display:none;'样式标签?

【问题讨论】:

【参考方案1】:

在 reactjs 中引用子项的正确方法 -> 不要引用子项来查询其状态。如果您需要从父级查询该状态,则将该状态放入父级并将该状态作为道具注入子级。

这是您的代码重做: 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 差异会更轻。除非您发现不渲染隐藏元素存在性能问题,否则不要对此进行优化。

【讨论】:

感谢您的回答!我在怀疑这个。我只是认为将切换状态保存在文件夹本身会很好,因为我认为它属于那里。但你是对的,嵌套所有状态然后让它们交互似乎是一个糟糕的概念。 @eyeballz 如果只有来自文件夹组件的事件可以更改此状态,则文件夹打开/关闭状态属于该文件夹。在您的示例中,toggleAll 事件不是来自文件夹组件,而是来自另一个。考虑“哪些组件可以改变哪些应用程序状态”是一个好主意。然后找到最近的公共父组件,并将状态放在这里,这样您就不必在任何地方查询子组件。

以上是关于在 reactjs 中引用子项的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

在 react js 中进行 API 调用的正确方法是啥?

在reactjs功能组件中实现contextType的正确方法是什么?

在 React 中使用 Jquery 的正确方法是啥?

当文本太多时,flexbox布局中的子项扩展到父项外部

对象作为 React 子对象无效。如果您打算渲染一组子项,请改用数组 - 错误 Solidity - React

ReactJS:React-Month-Picker错误:功能组件不能具有引用。您是要使用React.forwardRef()吗?