Реагуйте, щоб запобігти появі бульбашок подій у вкладених компонентах при натисканні


115

Ось базовий компонент. Як <ul>і, вони <li>мають функції onClick. Я хочу лише onClick на <li>вогонь, а не the<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 / ...

Відповіді:


162

У мене було те саме питання. Я знайшов 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

що робити, якщо вам потрібно передати параметри для обробкиClick?
Мантікоре

Чи це не відповідає неправильним запитанням? ОП попросило ListItem для обробки події. Рішення має список, який обробляє його. (Якщо я щось не розумію.)
Томас Джей Раш

57

React використовує делегування події з одним слухачем подій на документі для подій, які спливаються, як-от "натиснути" в цьому прикладі, що означає зупинити поширення неможливо; реальна подія вже розповсюджується до моменту, коли ви взаємодієте з нею в React. stopPropagation на синтетичній події React можливий, оскільки React здійснює внутрішнє поширення синтетичних подій.

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

супер корисний .. stopPropagation () сам по собі не працював належним чином
pravin

1
Це те, що працювало для мене, тому що я використовую в document.addEventListener('click')поєднанні з onClick
реакціями

2
Іноді це може не спрацювати - наприклад, якщо ви використовуєте бібліотеку на зразок Material-UI (www.material-ui.com) або обробляєте обробники в зворотному дзвінку іншого компонента. Але якщо ваш власний код безпосередньо, це добре!
rob2d

1
@ rob2d, то як з цим впоратися при використанні Material-UI?
Італік

@Italik є зразкова функція вище; але вам потрібно викликати його негайно, як перше, що відбувається у зворотному дзвінку, щоб він не віртуалізувався / проковтувався. Принаймні AFAIK. На щастя, останнім часом не билися з цим.
rob2d

9

На замовлення подій DOM: CAPTURING vs BUBBLING

Існують два етапи поширення подій. Вони називаються "захоплення" та "бурчання" .

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

Етап захоплення відбувається спочатку, а потім слідує стадія барботажу. Коли ви реєструєте подію за допомогою звичайного api-дому DOM, події за замовчуванням будуть частиною барботного етапу, але це можна вказати при створенні події

// 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>

Давайте заглянемо всередину нашого зворотного дзвінка Click (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 пул . Отже, через те, що eventпройдене в React насправді не існує до моменту, коли ви досягнете цього! Це трапляється в онуків через те, як реагувати внутрішньо обробляє речі, попередньо перевіривши батьків наSyntheticEvent обробників (особливо якщо у батька був зворотний виклик).

Поки ти не дзвониш persist щось, що створило б значну пам’ять, щоб продовжувати створювати такі події, якonMouseMove (а ви не створюєте якусь гру Cookie Clicker, як "Бабусині куки"), це повинно бути прекрасно!

Також зауважте: час від часу читаючи їх 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 для посилань, які зупиняють основну функціональність посилання, але не для бабінгу подій.
Джоел
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.