Реагировать на предотвращение всплытия событий во вложенных компонентах при нажатии


116

Вот базовый компонент. И у <ul>и <li>есть функции onClick. Я хочу, чтобы запускался только onClick <li>, а не файл <ul>. Как я могу этого добиться?

Я играл с e.preventDefault (), e.stopPropagation (), но безрезультатно.

class List extends React.Component {
  constructor(props) {
    super(props);
  }

  handleClick() {
    // do something
  }

  render() {

    return (
      <ul 
        onClick={(e) => {
          console.log('parent');
          this.handleClick();
        }}
      >
        <li 
          onClick={(e) => {
            console.log('child');
            // prevent default? prevent propagation?
            this.handleClick();
          }}
        >
        </li>       
      </ul>
    )
  }
}

// => parent
// => child

У меня был такой же вопрос, stackoverflow.com/questions/44711549/…

Ответы:


164

Я была такая же проблема. Я обнаружил, что stopPropagation действительно работает. Я бы разделил элемент списка на отдельный компонент, вот так:

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

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

class ListItem extends React.Component {
  handleClick = e => {
    e.stopPropagation();  //  <------ Here is the magic
    this.props.onClick();
  }

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

Не должно быть this.props.handleClick()в ListItemкомпоненте this.props.click?
blankface

3
Любой другой способ, кроме stopPropogation?
Али Саджид

Как насчет нативного компонента, такого как<Link>
Samayo 06

что, если вам нужно передать параметры в handleClick?
Manticore

Разве это не ответ на неправильный вопрос? OP запросил ListItem для обработки события. В решении есть список, обрабатывающий его. (Если я чего-то не понимаю.)
Томас Джей Раш,

57

React использует делегирование событий с одним прослушивателем событий в документе для всплывающих событий, таких как «щелчок» в этом примере, что означает, что остановка распространения невозможна; реальное событие уже распространено к тому моменту, когда вы взаимодействуете с ним в React. stopPropagation в синтетическом событии React возможно, потому что React обрабатывает распространение синтетических событий внутри себя.

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
}

супер полезно .. stopPropagation () сам по себе не работал должным образом
правин

1
Это то, что сработало для меня, потому что я использую в document.addEventListener('click')сочетании с onClick
react

2
Иногда это может не сработать - например, если вы используете такую ​​библиотеку, как Material-UI (www.material-ui.com), или обертываете свои обработчики в обратном вызове другого компонента. Но если ваш собственный код напрямую, это нормально!
rob2d 03

1
@ rob2d, так как с этим бороться при использовании Material-UI?
Италик,

@Italik есть пример функции выше; но вам нужно вызвать его немедленно как первое действие в обратном вызове, чтобы он не был виртуализирован / проглочен. По крайней мере, AFAIK. К счастью в последнее время не боролся с этим.
rob2d

9

По порядку событий DOM: ЗАХВАТ vs ЗАПУСК

Есть два этапа распространения событий. Это называется «захват» и «всплытие» .

               | |                                   / \
---------------| |-----------------   ---------------| |-----------------
| element1     | |                |   | element1     | |                |
|   -----------| |-----------     |   |   -----------| |-----------     |
|   |element2  \ /          |     |   |   |element2  | |          |     |
|   -------------------------     |   |   -------------------------     |
|        Event CAPTURING          |   |        Event BUBBLING           |
-----------------------------------   -----------------------------------

Сначала происходит этап захвата, затем следует этап всплывания. Когда вы регистрируете событие с помощью обычного DOM api, события по умолчанию будут частью стадии восходящей цепочки, но это можно указать при создании события.

// CAPTURING event
button.addEventListener('click', handleClick, true)

// BUBBLING events
button.addEventListener('click', handleClick, false)
button.addEventListener('click', handleClick)

В React всплывающие события также используются по умолчанию.

// handleClick is a BUBBLING (synthetic) event
<button onClick={handleClick}></button>

// handleClick is a CAPTURING (synthetic) event
<button onClickCapture={handleClick}></button>

Заглянем внутрь нашего обратного вызова handleClick (React):

function handleClick(e) {
  // This will prevent any synthetic events from firing after this one
  e.stopPropagation()
}
function handleClick(e) {
  // This will set e.defaultPrevented to true
  // (for all synthetic events firing after this one)
  e.preventDefault()  
}

Альтернатива, которую я здесь не видел

Если вы вызываете e.preventDefault () во всех своих событиях, вы можете проверить, было ли событие уже обработано, и предотвратить его повторную обработку:

handleEvent(e) {
  if (e.defaultPrevented) return  // Exits here if event has been handled
  e.preventDefault()

  // Perform whatever you need to here.
}

Информацию о разнице между синтетическими и собственными событиями см. В документации React: https://reactjs.org/docs/events.html


5

Это не на 100% идеал, но если это слишком сложно передать propsдетям -> моде для детей, или создать Context.Provider/ Context.Consumer только для этой цели), и вы имеете дело с другой библиотекой, у которой есть собственный обработчик, который она запускает перед вашим вы также можете попробовать:

   function myHandler(e) { 
        e.persist(); 
        e.nativeEvent.stopImmediatePropagation();
        e.stopPropagation(); 
   }

Насколько я понимаю, этот event.persistметод предотвращает немедленное попадание объекта обратно в SyntheticEventпул React . Так что из-за этого eventпереданного в React фактически не существует к тому времени, когда вы его доберетесь! Это происходит с внуками из-за того, как React обрабатывает вещи внутренне, сначала проверяя родительский SyntheticEventэлемент на наличие обработчиков (особенно если у родителя был обратный вызов).

Пока вы не вызываете persistчто-то, что могло бы создать значительную память для создания таких событий, как onMouseMove(и вы не создаете какую-то игру Cookie Clicker, такую ​​как Cookies бабушки), все должно быть в полном порядке!

Также обратите внимание: время от времени читая их GitHub, мы должны следить за будущими версиями React, поскольку они могут в конечном итоге решить некоторые проблемы с этим, поскольку они, похоже, собираются сделать складной код React в компиляторе / транспиляторе.


1

У меня были проблемы с event.stopPropagation()работой. Если вы тоже это сделаете, попробуйте переместить его в верхнюю часть функции обработчика кликов, это было то, что мне нужно было сделать, чтобы остановить всплытие события. Пример функции:

  toggleFilter(e) {
    e.stopPropagation(); // If moved to the end of the function, will not work
    let target = e.target;
    let i = 10; // Sanity breaker

    while(true) {
      if (--i === 0) { return; }
      if (target.classList.contains("filter")) {
        target.classList.toggle("active");
        break;
      }
      target = target.parentNode;
    }
  }

0

Вы можете избежать всплытия событий, проверив цель события.
Например, если у вас есть ввод, вложенный в элемент div, где у вас есть обработчик для события щелчка, и вы не хотите его обрабатывать, при щелчке ввода вы можете просто перейти event.targetв свой обработчик и проверить, должен ли обработчик выполняться на основе свойства цели.
Например вы можете проверить if (target.localName === "input") { return}.
Итак, это способ «избежать» выполнения обработчика


-4

Новый способ сделать это намного проще и сэкономит вам время! Просто передайте событие в исходный обработчик кликов и вызовите preventDefault();.

clickHandler(e){
    e.preventDefault();
    //Your functionality here
}

3
-1; Я тестировал это, и он не работает для распространения кликов. AFAIK, это будет полезно, если вам нужен обработчик onClick для ссылок, которые останавливают базовую функциональность ссылок, но не для восходящей цепочки событий.
Joel Peltonen
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.