onClick on sub <li>做出反应

时间:2017-04-10 15:25:03

标签: javascript list reactjs

我想在列表中添加带有反应的子类别,但是当我点击子猫时,我有两个事件:第一个是子类,第二个是父类。

我怎样只有子类别?

有我的实际代码:

getList(myList){
    return myList.map((item) => {
        let subList = '';
        if (item.hasSub){
            subList = (<ul>{this.getList(item.sub)}</ul>)
        }
        return (
        <li onClick={() => {this.props.clicHandler(item.id)}}>{item.title}<div>{subList}</div></li>);
    })
}

我使用递归方法创建列表。 我的实际数组是这样的:

this.list.push({id: 1, title:'coucou' , hasSub: false});
this.list.push({id: 2, title:'toto' , hasSub: false});
this.list.push({id: 3, title: 'cat', sub: [{id:4, title:'titi' , hasSub: false}, {id:5, title:'tutu' , hasSub: false}] , hasSub: true});

感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

您需要阻止事件传播:

getList(myList){
    return myList.map((item) => {
        let subList = '';
        if (item.hasSub){
            subList = (<ul>{this.getList(item.sub)}</ul>)
        }
        return (
        <li onClick={(event) => {event.preventDefault(); this.props.clicHandler(item.id)}}>{item.title}<div>{subList}</div></li>);
    })
}

这样,捕获点击的第一个项目将停止点击传播。

答案 1 :(得分:0)

问题在于click events "bubble" up到父元素。解决方案是在其上调用Event.prototype.stopPropagation

在下面的代码段中,我为组件添加了clickHandler方法,并在getList中将事件(evt)和item.id传递给它。在该方法中,我在致电evt.stopPropagation()之前致电this.props.clickHandler。但是,您也可以在父组件的clickHandler方法或getList内执行此操作。选择最适合您用例的任何内容。

class App extends React.Component {
  constructor() {
    super();
    this.clickHandler = this.clickHandler.bind(this);
  }
  
  render() {
    return <ul>{this.getList(this.props.items)}</ul>;
  }

  getList(list) {
    return list.map((item) => {
      const subList = item.hasSub && (<ul>{this.getList(item.sub)}</ul>);
      return (
        <li key={item.id} onClick={evt => {this.clickHandler(evt, item.id)}}>{item.title}<div>{subList}</div></li>
      );
    });
  }
  
  clickHandler(evt, itemId) {
    // Stop event propagatation before calling handler passed in props
    evt.stopPropagation();
    this.props.clickHandler(itemId);
  }
}

const data = [
  { id: 'a', title: 'A', hasSub: false },
  { id: 'b', title: 'B', hasSub: true, sub: [
    { id: 'b1', title: 'B1', hasSub: true, sub: [
      { id: 'b1a', title: 'B1a', hasSub: false },
    ] },
    { id: 'b2', title: 'B2', hasSub: false },
  ] },
  { id: 'c', title: 'C', hasSub: true, sub: [
    { id: 'c1', title: 'C1', hasSub: false },
    { id: 'c2', title: 'C2', hasSub: false },
  ] },
];

function clickHandler(itemId) {
  console.log('clicked item', itemId);
}

ReactDOM.render(<App items={data} clickHandler={clickHandler}/>, document.querySelector('div'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div></div>

P.S。您的代码非常好,但我可能会建议使用更多“React方式”来呈现嵌套项吗?

您的组件的render方法所做的是调用 getItems函数,然后递归调用自身。但是,如果您还记得大多数React组件本身都可以被视为函数,那么您可以重构代码,以便您的render方法呈现递归呈现的 <Items>组件本身的实例。

这种方法倾向于产生更小的功能组件,这些组件更易于重复使用且更易于测试。请查看下面的代码段。

const Items = ({items, onClick}) => (
  <ul>
    {items.map(item => (
      <li key={item.id} onClick={evt => onClick(evt, item.id)}>
        {item.title}
        {item.hasSub && <Items items={item.sub} onClick={onClick}/>}
      </li>
    ))}
  </ul>
);

const data = [
  { id: 'a', title: 'A', hasSub: false },
  { id: 'b', title: 'B', hasSub: true, sub: [
    { id: 'b1', title: 'B1', hasSub: true, sub: [
      { id: 'b1a', title: 'B1a', hasSub: false },
    ] },
  ] },
];

function clickHandler(evt, itemId) {
  evt.stopPropagation();
  console.log('clicked item', itemId);
}

ReactDOM.render(<Items items={data} onClick={clickHandler}/>, document.querySelector('div'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div></div>