【问题标题】:Open and Close a recursively nested list in React js在 React js 中打开和关闭递归嵌套列表
【发布时间】:2023-03-17 16:05:02
【问题描述】:

我希望能够使用 React 打开/关闭嵌套列表,这样当您单击父 li 时,子项会被隐藏?这是我用来创建列表的内容。

list.js

class List extends React.Component {

render(){
    return (
        <ul>
            {this.props.list.map(function(item){
                return (
                <li>
                    <ListItem key={item.id} item={item} />
                    <List list={item.children} />
                </li>
                );
            })}
        </ul>
    );
}

list-item.js

class ListItem extends React.Component {

handleCollapse(){
    console.log('Open/Close: ' + this.props.item.display_name);
    return false;
}

handleFilter(){
    console.log('Filter id: ' + this.props.item.id);
    return false;
}

render(){
    return (
        <div>
            <a rel="{this.props.item.id}" onClick={this.handleCollapse.bind(this)}>
                {this.props.item.display_name}  
            </a>
            <input value="" type="checkbox" onClick={this.handleFilter.bind(this)} />
        </div>
    )
}

【问题讨论】:

    标签: javascript list reactjs nested


    【解决方案1】:

    您是否在尝试模拟手风琴行为?如果是,那么您可以像这样修改您的代码。使用组件的状态并切换它以打开和关闭子项。不要在List 类中创建&lt;List list={item.children} /&gt;,而是在list-item.js 中导入(或使用requirelist.js 并在当前ListItem's 状态的基础上渲染子List 项。

    list-item.js

    class ListItem extends React.Component {
      //Add this
      constructor (){
          super(...arguments);
          this.state = { showChild:false};
      }
    
      handleCollapse(){
          console.log('Open/Close: ' + this.props.item.display_name);
          //Add this
          this.setState({showChild:!this.state.showChild});
          return false;
      }
    
      handleFilter(){
          console.log('Filter id: ' + this.props.item.id);
          return false;
      }
    
      render(){
          let children;
          if(this.state.showChild) {
              children = (<List list={this.props.item.children} />);
          }
    
          return (
              <div>
                  <a rel="{this.props.item.id}" onClick={this.handleCollapse.bind(this)}>
                {this.props.item.display_name}
                  </a>
                  <input value="" type="checkbox" onClick={this.handleFilter.bind(this)} />
                 //Add this
                 {children}
              </div>
          )
      };
    }
    

    list.js

    class List extends React.Component {
    
        render(){
            //Removed <List .../>, rest is same just other way of doing same stuff
            let LI = this.props.list.map( (item) => {
                return( <li> <ListItem key={item.id} item={item} /></li>);
            }
         );
    
            return ( <ul>{LI}</ul>);
      }
    
    };
    

    要测试的虚拟数据

    var data=[
      {
          id:"1st of Level 1",
          get display_name(){return _name(this.id,this.children)},
          children: [
              {
                  id:"1st of Level 1.2",
                  get display_name(){return _name(this.id,this.children)},
                  children: [
                      {
                          id:"1st of Level 1.2.1",
                          get display_name(){return _name(this.id,this.children)},
                          children:[]
                      }
                  ]
              },
    
              {
                  id:"2nd of Level 1.2",
                  get display_name(){return _name(this.id,this.children)},
                  children:[]
              }
          ]
      },
    
      {
          id:"2nd of Level 1",
          get display_name(){return _name(this.id,this.children)},
          children:[]
      },
    
      {
          id:"3rd of Level 1",
          get display_name(){return _name(this.id,this.children)},
          children:[]
       },
    
       {
           id:"4th of Level1",
           get display_name(){return _name(this.id,this.children)},
           children:[]
       }
    
     ];
    
    function _name(id,child) {
        if(child.length>0)
            return("I am "+id+" I HAVE CHILDREN");
        else {
            return("I am "+id+" I DON'T HAVE CHILDREN ");
        }    
    }
    
    
    ReactDOM.render(<List list={data} />,document.getElementById('root'));
    

    `

    【讨论】:

      【解决方案2】:

      递归执行此操作是解决此问题的好方法。您可以做的是给每个列表项元素一个onClick 处理程序。

      <li onClick={this._toggleChildrenInit.bind(this, item.id)}>
      

      我不太了解你的身份。但请考虑以下结构:

      {id: "0", isOpen: false, children: [
        {id: "0.0", isOpen: false, children: [
          {id: "0.0.0", isOpen: false, children: null},
          {id: "0.0.1", isOpen: false, children: null}
        ]},
        {id: "0.1", isOpen: false, children: null}
      ]}
      

      每个孩子的 id 是前一个父母的 id 加上一个“.”以及该级别内的订单数。

      然后,您可以通过递归检查具有某个 X id 的所需项目是否可能是某个项目的子项,并以相同的方式开始遍历该子项。这样,您不必遍历树中的所有项目。

      当然,这可能因应用程序而异,但希望它为您可以提供一个线索。

      点击触发的递归代码如下:

      _toggleChildrenInit = (data_id) => {
        var result = this._toggleChildren(0, data_id, this.state.listObject);
       this.setState({listObject: result});
      }
      
      _toggleChildren = (i, data_id, arr) => {
        if (i <= arr.length-1) {
          if (arr[i].id == data_id && arr[i].children.length > 0) {
            arr[i].isOpen = !arr[i].isOpen;
          } else {
            var newArr = this._toggleChildren(0, data_id, arr[i].children);
            arr[i].children = newArr;
            if (arr[i].id.charAt(0) != data_id.charAt(0)) {
              arr[i].isOpen = false;
            }
          }
          arr = this._toggleChildren(++i, data_id, arr);
        }
        return arr;
      }
      

      编辑 - 替代结构

      如果你的对象看起来像这样:

      {id: "0", isOpen: false, children: [
        {id: "1", isOpen: false, children: [
          {id: "2", isOpen: false, children: null},
          {id: "3", isOpen: false, children: null}
        ]},
        {id: "4", isOpen: false, children: null}
      ]}
      

      您可以改用这个:

      _toggleChildrenInit = (data_id) => {
        var result = this._toggleChildren(0, data_id, this.state.listObject);
       this.setState({listObject: result});
      }
      
      _toggleChildren = (i, data_id, arr) => {
        if (i <= arr.length-1) {
          if (arr[i].id == data_id && arr[i].children.length > 0) {
            arr[i].isOpen = !arr[i].isOpen;
          } else {
            var newArr = this._toggleChildren(0, data_id, arr[i].children);
            arr[i].children = newArr;
            if (arr[i].id < data_id) {
              arr[i].isOpen = false;
            }
          }
          arr = this._toggleChildren(++i, data_id, arr);
        }
        return arr;
      }
      

      【讨论】:

      • 是的,差不多。一个小的调整是必要的。我尚未对此进行测试,但请尝试上面的编辑。
      • 很奇怪。您应该拥有onClick,就像您在代码中拥有自己的事件处理程序一样。尝试控制台记录this
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-17
      • 1970-01-01
      • 2021-09-19
      • 1970-01-01
      • 1970-01-01
      • 2018-06-24
      相关资源
      最近更新 更多