-
[React.js] Event Handling(이벤트 처리)Front-End(Web)/React - 프레임워크(React, Next) 2021. 3. 15. 06:25반응형
React에서 컴포넌트를 만들면서, 다양한 이벤트와 연계해야되는 경우가 많이 발생한다. (클릭, input change 등)
특히, 이번 3차 프로젝트를 하면서, Navbar 메뉴들의 마우스 오버가 바뀔때마다 표현되는 서브메뉴가 달라지도록 만들어야했고, 여기에 하강 애니메이션까지 추가되야했다.
이를 구현하기 위해, React의 mouse 이벤트 종류와 차이점을 제대로 공부하는 기회가 되었고, 이벤트에 관한 포스팅 필요성을 느꼈다.
📒 이벤트 처리하기
React 요소의 이벤트 핸들링은 DOM에 대한 이벤트 핸들링과 매우 유사하다. 하지만, 아래와 같은 차이점은 있다.
- 이벤트 명을 소문자가 아닌 cammelCase로 작성한다. onclick(x) => onClick(o)
- 이벤트 핸들러를 문자열이 아닌 함수로 전달한다. onclick="handleClick()" (x) => onClick={handleClick()} (o)
- 이벤트 기본동작 방지
HTML의 태그 중에는 기본동작 이벤트를 가진 태그들이 있다.
<a> 태그는 click 시 href 링크로 페이지를 이동하거나, <form> 태그는 submit 시 페이지를 리로드하는 등이 해당된다.
DOM의 경우에는 이벤트 핸들러가 false를 반환하면 기본동작이 발생하지 않으나, React는 무조건적으로 발생한다.
이를 방지하기 위해, React에서는 preventDefault() 라는 메서드를 활용할 수 있다.
const handleClick = (e) => { e.preventDefault(); console.log("Link is Clicked.") } <a href="#" onClick={handleClick}> Click Me! </a>
이처럼, 이벤트 핸들러 로직 최상단에 e.preventDefault() 를 추가하면, 기본동작은 방지된 채 console만 작동한다.
여기서 인자로 사용되는 e(혹은 event)는 합성 이벤트(SyntheticEvent)이다.
이는, 모든 브라우저에서 이벤트를 동일하게 처리하기 위한 이벤트 Wrapper 객체로 아래 공식문서를 참고바란다.
* 링크 : React 공식문서 한글 번역(ko.reactjs.org/docs/events.html)
📒 이벤트 심화 다루기
- 클래스형 컴포넌트 : 이벤트에서의 this
class Toggle extends React.Component { constructor(props) { super(props); this.state = {isToggleOn: true}; this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn })); } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'ON' : 'OFF'} </button> ); } } ReactDOM.render( <Toggle />, document.getElementById('root') );
오랜만에 다시 보는 클래스형 컴포넌트이다. ㅎㅎ 😮😮
이처럼, 컴포넌트의 정의된 메서드를 사용할 때 constructor에서의 this 바인딩 처리가 필수적이었다. 왜일까?
메서드가 JSX를 통해 각 DOM에 이벤트로 등록되는 과정에서, 컴포넌트 인스턴스의 관계가 끊기게 된다.
그렇기에, 클릭시 this는 undefined가 될 것이며, handleClick 이벤트 핸들러 내에서도 this를 인식할 수 없다.
그래서 위처럼, 처음 constructor() 메서드에서 이벤트 핸들러에 현재 컴포넌트인 this를 바인딩 해주는 것이다!
- 추가적인 this 바인딩 방법
1) 이벤트에서 인라인 바인딩
<button onClick={this.handleClick.bind(this)}> Click me! </button>
이처럼, this(컴포넌트)와 연계가 필요한 이벤트 및 핸들러를 바로 바인딩해주는 방법이 있다.
2) ES6 Arrow Function 활용
class LoginButton extends React.Component { handleClick = () => { console.log(this) } render() { return ( <button onClick={this.handleClick}> Click me! </button> ) } }
이처럼 이벤트 핸들러를 Arrow Function 형태로 정의한다.
ES6의 Arrow Function 문법이 this bind를 지원하기 때문에 별도의 바인딩이 필요없는 것이다. 다른 방법도 있다!
class LoginButton extends React.Component { handleClick(e) { console.log(this, e.target) } render() { return ( <button onClick={(e) => this.handleClick(e)}> Click me! </button> ) } }
보시다시피, 차이점은 일반함수 형태로 선언했으며, JSX 적용시 anonymous Arrow Function으로 적용한 차이이다.
인라인 바인딩과 유사하다고 생각하면되며, 위 방법에 비해 babel 컴파일 에러발생 확률이 낮다는 장점이 있다.
하지만, 이 LoginButton 컴포넌트가 리렌더될 때마다 anonymous 함수가 생성되므로, 이벤트 핸들러가 자식 컴포넌트로 props를 전달하거나, 해당 컴포넌트가 대용량 리렌더링이 필요한 경우 성능상의 저하가 발생할 수 있다.
- 이벤트 핸들러에 인자 전달하기
const handleClick = (e, id) => { console.log(e.target.value, id) } return ( <input type="text" onChange={(e) => handleClick(e, 1)} /> )
일반적인 함수 사용법과 동일하다. 이벤트 핸들러에서 사용할 인자명을 함수선언시 정의해준다.
그리고, 핸들러 적용시에 해당 인자값을 전달해주면 된다.
합성 이벤트를 전달할때는, 위처럼 anonymous의 인자로 전달해주어야 해당 컴포넌트임을 인지할 수 있다.
본래는 이벤트 종류를 정리하는게 주목적이었는데, SyntheticEvent의 영역에 포함되어 분량을 나누고자 이벤트 처리부분만 우선 정리하였다. 😅😅
지난 1차 프로젝트에서, 클래스형 컴포넌트를 활용하며 무의식적으로 this 바인딩을 했던 이유를 명쾌히 알게되었다.
또한, (e)로 무심코 넘긴 인자가 SyntheticEvent(합성 이벤트) 라는 표준화를 위한 React 객체라는 것도 새로이 배웠다.
이 배경이 토대가 되어야, React에서 지원하는 이벤트들을 정리할 수 있겠다고 생각되었다!
[출처]
- React 공식문서 번역본 : ko.reactjs.org/docs/handling-events.html
- gseok 님의 gitbook : gseok.gitbooks.io/react/content/bd80-bd84-bd80-bd84-c9c0-c2dd-b4e4/react-c5d0-c11c-event-handling-d558-b294-bc95-c815-b9ac.html
반응형'Front-End(Web) > React - 프레임워크(React, Next)' 카테고리의 다른 글
[React] useEffect 심화 학습 (0) 2021.04.17 [React.js] SyntheticEvent(합성 이벤트) (0) 2021.03.15 [React.js] Custom Hooks (Rules of Hooks) (0) 2021.03.11 [React.js/Side Lib.] React-Router (0) 2020.12.29 [React.js] React 소개(wecode) (0) 2020.12.28