Почему событие распространяется на родительский объект даже после вызова e.nativeEvent.stopPropagation в реакции? - PullRequest
0 голосов
/ 21 июня 2020

Я создал компонент Sidepanel в React и прикрепил прослушиватель кликов, чтобы закрыть панель, когда пользователь щелкает где-нибудь за пределами компонента Sidepanel, например:

function Sidepanel({ isOpen, children }) {
  const [isPanelOpen, setPanelOpen] = useState(isOpen);
  const hidePanel = () => setPanelOpen(false);
 
  ... 

  useEffect(() => {
    document.addEventListener('click', hidePanel);
    return () => {
      document.removeEventListener('click', hidePanel);
    };
  });

  return (
    <aside
      className={`side-panel ${isPanelOpen ? 'side-panel--open' : ''}`} 
      onClick={stopEventPropagation}
    >
      <div className="side-panel__body">{children}</div>
    </aside>
  );
}

function stopEventPropagation (e) {
  e.stopPropagation(); // <-- DOESN'T SEEM TO WORK
};

export default Sidepanel;

Но этот код работает не так, как ожидалось, поскольку панель начинает закрываться по щелчку даже внутри самого элемента <aside>. Казалось, что e.stopPropagation() ничего не сделал, поэтому я обновил код stopEventPropagation на:

function stopEventPropagation (e) {
  e.nativeEvent && e.nativeEvent.stopPropagation();
};

... который тоже не сработал. Но когда я сделал это:

function stopEventPropagation (e) {
  e.nativeEvent && e.nativeEvent.stopImmediatePropagation(); // <- IT WORKED!
};

... это сработало. Странно!

Я прочитал несколько документов о stopImmediatePropagation , которые я нашел в Google, и понял, что, хотя он заставляет код работать, здесь нет никакого смысла. Я что-то упускаю?

Ответы [ 3 ]

0 голосов
/ 21 июня 2020

Говоря о event.stopPropagation() или event.stopImmediatePropagation(), мы говорим о термине Event bubbling (или Bubbling для краткости). Поэтому убедитесь, что вы знаете об этом, в противном случае, пожалуйста, ознакомьтесь с этой статьей

В приведенной выше статье вы можете найти эту one :

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

1. event.stopPropagation() останавливает движение вверх, но на текущем элементе будут выполняться все остальные обработчики.

2. Чтобы остановить восходящую цепочку и предотвратить запуск обработчиков текущего элемента, существует метод event.stopImmediatePropagation(). После этого никакие другие обработчики не выполняются.

Возвращаясь к вашей проблеме, это относится к более позднему случаю. У вас есть два обработчика onClick, прикрепленных к document, поэтому использование event.stopPropagation() здесь не подходит, а event.stopImmediatePropagation().

--- Обновление 1:

React (в настоящее время ) использует прослушиватель на document для (почти) всех событий, поэтому к тому времени, когда ваш компонент получит событие, он уже всплывет до document.

Это означает, что более поздний Обработчик onClick, прикрепленный к элементу aside, будет всплывать до document.

0 голосов
/ 21 июня 2020

Если вы добавили прослушиватель событий обычным образом (с помощью window.addEventListener()), то e.stopPropagation() должен работать. Но если вы добавили его через React (по onClick), он не будет работать, потому что React использует собственную обработку событий, которая сама прослушивает document. Таким образом, к моменту запуска обработчика onClick на aside событие могло быть увеличено и достигло document, где был запущен ваш hidePanel() слушатель.

e.nativeEvent.stopImmediatePropagation() может отменить все слушатели раньше это происходит и решает вашу проблему, но слепой обход обработчиков событий (возможно, добавленных какой-либо библиотекой пользовательского интерфейса) может вызвать побочные эффекты. Таким образом, лучший подход, на который не влияет все запутанное распространение событий, может заключаться в обработке logi c в слушателе document click.

    const hidePanel = (e) => {
        // Only hide panel if clicked outside it
        if (!e.target.classList.contains('side-panel')) {
            setPanelOpen(false);
        }
    }
0 голосов
/ 21 июня 2020

Замените document.addEventListener на window.addEventListener, чтобы event.stopPropagation() могло остановить распространение события в окно.

Надеюсь, это вам поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...