ReactJS)이벤트 버블링

Sal Jeong·2022년 9월 20일
0

생각해보니... react에서는 일반적으로 props drilling을 통해서 eventHandler를 전달하는데, 그렇게 되는 것이 더 편한건 알겠지만, 왜 그렇게 하는 것인지에 대해서는 알지 못했다.

tr;dr

  1. 기존 html 이벤트 캡쳐링, 타게팅, 버블링과는 다르게 react에서는 capturing 단계가 없다.
  2. targeting -> bubbling의 순서로 이벤트가 실행되며, 이는 SyntheticEvents라는 Wrapper를 통해 이루어진다.
  3. 리액트 17버전 부터는 버블링된 이벤트가 맨 상위로 올라가는 것이 아니라 root element까지만 올라간다.

1. 질문 변경, 이벤트 버블링이 무엇이고 어떻게 일어나나?

기존 이벤트 버블링, 캡쳐링 프로세스는

  1. 캡쳐링. 이벤트가 트리거되었을 때, event.target을 찾기 위해서 맨 상위부터 아래까지 이벤트 리스너 실행(window -> document -> html ... -> event.target)
  2. 타게팅. event.target에 도달했을 때, 어떤 엘리먼트에서 실행되었는지 bind함.
  3. 버블링. event.target부터 window 오브젝트까지 올라가면서 상위의 eventListener들을 건드리게 됨.(다만 맨 상위의 event는 다시 call되지 않음)
$('.add-button').click(function() {
	$('.container').append('<button class="button">Click Me</button>');
})

$('.container').on('click', '.button', function() {
	alert('Hello!');
})

react 에전 jquery의 시대에는 이러한 패턴을 자주 썼다고 한다.
상위에 리스너를 하나 걸어놓으면, 아래에서 일어나는 이벤트를 모두 확인 가능했기 때문에

react에서는?

2. 그렇다면 리액트에서 event bubbling이란?

class Card extends React.Component {
	onClickHandler = () => {
  	alert('Hello World!');
  }
  render() {
  	return (
    	<div
        className='card'
        onClick={ this.onClickHandler }
      >
        <img 
          className='icon'   
          src='https://facebook.github.io/react/img/logo.svg'
        />
        <h2 className='title'>Why React discourage event delegation?</h2>
        <p className='excerpt'>Experienced frontend developers should be familiar with event bubbling and event delegation...</p>
    	</div>
    );
  }
}

class App extends React.Component {
  render() {
  	return (
    	<div>
        <Card/>
    	</div>
    );
  }
}

ReactDOM.render(<App name="world"/>, document.getElementById('container'));

리액트 역시 버블링은 동일하게 동작한다.

위 코드를 본다면, Card class의 상위 div에 onCLick을 하나 걸어놓는 것으로 nested된 모든 엘리먼트에 클릭 이벤트가 위에서 참조할 수 있게 된다.

하지만 1. 예시와 다른 점은 리액트에서는 이와는 다르게 SyntheticEvent라는 방식으로 이벤트를 다룬다.

SyntheticEvent는 브라우져의 이벤트 오브젝트의 Wrapper이며, stopPropagation(버블링 막음)이나 preventDefault(새로고침 막음)같은 메소드도 사용 가능하다.

가장 큰 차이점은 react에서는 2. 예시처럼 이벤트 핸들링 시 모든 노드에 핸들러를 붙이지 않는다는 것이다.

리액트에서는 위와 같은 방식(맨 상위부터 아래로 내려가는 방식) 대신,
1. 이벤트를 클릭할 시 타켓 엘리먼트를 먼저 부른다(타게팅 단계),
2. 그리고 버블링한다.

하지만 onClickCapture를 이용해서 capturing을 활성화 시킬 수도 있다.

import React, { Component } from "react";

class Molly extends Component {
    ...
// 일반적으로 캡쳐링을 이용하려면 elem.addEventListener(..., {capture: true}) 식이지만
// react에서는 onClickCapture를 사용하지 않으면 capturing 단계가 일어나지 않음.
    render() {
        return (
        
            <div className="im-a-parent" onClickCapture={this.handleCallFamilyToEat}> 
                <button className="im-a-child" onClick={this.handleCookEggs}>Cook Eggs</button>
                <button className="im-a-child" onClick={this.handleMakeRice}>Make Rice</button>
                <button className="im-a-child" onClick={this.handleMixChicken}>Mix Chicken</button>
            </div>
        );
    }

}

export default Molly;

p.s.)
react fiber 도입된 버전은 16.

리액트 16버전까지는 SyntheticEvents가 실제 dom tree 처럼 버블링시 맨 상위 버전까지 버블링이 일어났다. eventListener를 모든 요소에 붙이게 된다는 것.

하지만 리액트 17버전 부터는 이벤트 자체가 react의 root element (index.html에 있는) 까지만 전달된다고 한다.

참고한 url

https://blog.cloudboost.io/why-react-discourage-event-delegation-2b5fe3f52bea
profile
Can an old dog learn new tricks?

0개의 댓글