javascript - 使用 map 触发多个 onClick 事件监听器的嵌套列表

标签 javascript reactjs

我正在从 json 文件生成嵌套列表。我将该 json 文件转换为数组树。在这里,我想使用那个数组树制作一个下拉列表,其中 onSidebarComponent 根据它显示数据。我使用数组映射并将数据插入数组并显示它们。

但是每当我单击第一个索引数据时,它都会触发一次单击事件。每当我单击叶子节点数据时,它都会触发 3 个单击事件。另一方面,我不知道如何处理动态显示和隐藏单个列表数据。为了更好地理解,我提供了我的代码和一些屏幕截图。

代码如下:

import React, { Component } from 'react';
import './App.css';
import data from './data/categories.json'
import { list_to_tree, search_from_tree } from './data/supportFunc'

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      search: "",
      show1: false,
      show2: false,
      show3: false
    }
  }
  //----------------------------------------------------------------------------------------
  onChangeHandler(e) {
    this.setState({
      search: e.target.value
    })
  }

  //----------------------------------------------------------------------------------------
  onNavPressed1(e, i) {
    e.preventDefault()
    console.log("button pressed1")
    console.log(i)
    this.setState({
      show1: i,
      search: ""
    })
  }
  //----------------------------------------------------------------------------------------
  onNavPressed2(e, i) {
    e.preventDefault()
    console.log("button pressed2")
    console.log(i)
    this.setState({
      show2: i,
    })
  }
  //----------------------------------------------------------------------------------------
  onNavPressed3(e, i) {
    e.preventDefault()
    console.log("button pressed3")
    console.log(i)
    this.setState({
      show3: i
    })
  }
  //----------------------------------------------------------------------------------------
  sideBarComponent(listData) {
    if (this.state.search !== "") {
      var clsName = "navbar-nav padd"
    }
    else {
      var clsName = "navbar-nav padd collapse"
    }

    let arr = []
    listData.map((d, i) => {
      let subarr = []
      d.children.map((child, j) => {
        let subChildArr = []
        child.children.map((subChild, k) => {
          subChildArr.push(<li onClick={(e) => this.onNavPressed3(e, "_" + k)} className="nav-item listItem" key={i + "_" + j + "_" + k}>
            {subChild.Name} <span className="float-right"> {subChild.children.length !== 0 ? <i className="fas fa-angle-right"></i> : " "}</span>
          </li>)
        })
        subarr.push(<li onClick={(e) => this.onNavPressed2(e, "_" + j)} className="nav-item listItem" key={i + "_" + j}>
          {child.Name} <span className="float-right"> {child.children.length !== 0 ? <i className="fas fa-angle-right"></i> : " "}</span>
          <ul className={clsName}>{subChildArr}</ul>
        </li>)
      })
      arr.push(<li onClick={(e) => this.onNavPressed1(e, "_" + i)} className="nav-item listItem" key={i}>
        {d.Name} <span className="float-right ">{d.children.length !== 0 ? <i className="fas fa-angle-right"></i> : " "}</span>
        <ul className={clsName}>{subarr}</ul>
      </li>)
    })
    return (<nav className="navbar">
      <ul className="navbar-nav navbar-width">
        {arr}
      </ul>
    </nav>
    )
  }
  //----------------------------------------------------------------------------------------
  render() {
    var val = this.state.search.toUpperCase()
    var listData = list_to_tree(data)
    var newListData = search_from_tree(listData, val)
    // console.log("listData======")
    // console.log(newListData)
    return (
      <div className="App">
        <header className="App-header">
          <input type="text"
            name="search"
            className="App-search"
            onChange={(e) => this.onChangeHandler(e)}
            value={this.state.search}
            placeholder="Enter Products Category"
          />
        </header>
        <div className="nav-container">
          {this.sideBarComponent(newListData)}
        </div>
      </div>
    );
  }
}

export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

这是截图 enter image description here

最佳答案

发生了什么?

该行为是由 the event propagation 引起的.事件传播有两种类型 - 事件冒泡和捕获。

事件冒泡 - 首先触发最内层元素的事件,然后将事件传播到外层元素,即inside -> out

事件捕获 - 首先触发最外层元素的事件,然后将事件传播到内部元素,即out -> inside

解决方案:

根据您的用例,这里我们讨论的是事件冒泡。因此,为了防止事件传播到父元素,您必须通过嵌套项目 onClick 处理程序中的 e.stopPropagation() 停止事件传播。

我想在您的 onNavPressed 处理程序中调用 e.stopPropagation() 是完全没问题的。

但是,这里有一个基本示例,更清晰地说明了如何防止 React 中的事件冒泡 (credits):

class List extends React.Component {
  handleClick = e => {
    // do something
  }

  render() {
    return (
      <ul onClick={this.handleClick}>
        <ListItem onClick={this.handleClick}>Item</li> 
      </ul>
    )
  }
}

class ListItem extends React.Component {
  handleClick = e => {
    e.stopPropagation();
    this.props.onClick();
  }

  render() {
    return (
      <li onClick={this.handleClick}>
        {this.props.children}
      </li>       
    )
  }
}

学分:

  1. What is event bubbling and capturing?
  2. React prevent event bubbling in nested components on click
  3. Example for Bubbling and Capturing in React.js

关于javascript - 使用 map 触发多个 onClick 事件监听器的嵌套列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50819433/

相关文章:

javascript - Mongoose 返回一个空数组

jquery - ReactJS - 在表格行上使用 slideDown(切换)?

javascript - 如何自动向 React 元素添加适当的类名

reactjs - 使用 React Router 加载组件 你不应该在 <Router> 之外使用 <Link>

javascript - 根据网络带宽自动选择合适的视频质量?

javascript - Angular Breeze 设置

javascript - 仅当从特定页面链接时才运行 Javascript 函数

javascript - Jquery Unobtrusive 验证不适用于 View 模型列表中第一个之后的项目 - c# MVC 4

javascript - 无法在reactjs中正确地处理对象数组中的 'map'数组

javascript - 无法在React中访问窗口变量