이벤트 처리하기

Yeom Jae Seon·2021년 2월 3일
1

React공식문서 공부

목록 보기
5/11
post-thumbnail

지난시간까지


지난시간엔 State를 사용해 ReactDOM.render()함수를 여러번 호출하지 않아도 컴포넌트 자체에서 setState를 이용해 새롭게 DOM 구성 -> 최신 UI로 업데이트 즉, 렌더링을 시켰다.
그 과정에서 생명주기함수도 사용해보았고 state사용할때 주의할점, 클래스 컴포넌트도 작성해 보았다.

React Element에서 이벤트 처리하기


이번엔 이벤트를 처리하는 법을 배워보자.

결론부터 말하자면 DOM 엘리먼트에 oneline으로 이벤트를 처리하는 방식과 매우 유사하다.
(거의 비슷한데 몇가지 다른게 있기 때문이다.)

그 몇가지가 뭐냐면?

  • React Element에 이벤트를 주는 방법은 소문자 대신 camelCase를 사용한다.
  • DOM Element에서 oneline으로 문자열로 이벤트를 주는 것과는 다르게 JSX에서는 함수로 이벤트핸들러를 전달한다.(JSX시간에 속성 주는법 {}를 분명 배웠다.)
  • 브라우저가 기본적으로 제공하는 기능을 막으려면 React Element에선 preventDefault()함수를 명시적으로 써야한다.

DOM Element에 이벤트를 주는방식이다.!

<button onclick="increaseFunc()">INCREMENT</button>

반면에 React Element에 이벤트를 주는 방식

<button onClick={increaseFunc}>INCREMENT</button>

거의 비슷하다. 하지만 몇가지 차이가 존재한다.
이 차이들은 JSX가 객체로 인식된다는것 즉, React Element를 생성한다는 개념을 알고있으면 별로 의아한것도 아니다.

그리고 등록된 이벤트 핸들러에 전달되는 event객체를 React에서는 합성 이벤트라고 한다.
이 합성이벤트를 통해서 브라우저 마다 다른 호환성을 걱정할 필요가 없다.
React에서는 DOM Element에서 사용하던 브라우저 고유 이벤트와는 다르기 때문에 합성 이벤트와 동일하지 않다는걸 알아야한다.
(고유 이벤트를 사용하려면 nativeEvent를 알아봐서 사용하자)

그리고 React Element에서는(React를 사용할때) 최초 랜더링 된 이후에 리스너를 추가하기 위해 addEventListener를 호출할 필요가 없다. 이벤트를 받고자 하는 React Element가 처음 랜더링 될때(ReactDOM.render()에 의해서겠네. 처음렌더링이닌까.) 리스너를 제공하면 계속 리스너가 등록되어있다.
위의 예에선 increaseFunc가 리스너로 계속 등록되어 있겠군!

사실 이차이는 JS를 알면 알수있다.
DOM Element에 oneline으로 이벤트리스너 등록한 경우에는 보면 increaseFunc()이라는 리턴된 값을 전달했다.
대신 React Element에서 이벤트처리하기위해 등록한 리스너는 increaseFunc이다.
이는 함수이고 함수는 reference 즉, 함수블럭을 가르키는 주소를 가지고있다. 당연히 어떠한 값을 등록한게아니라 주소를 등록했으므로 Dom Element에서 이벤트 처리한거처럼 oneline으로 이벤트처리해도 addEventListener처럼 이벤트 리스너가 계속 등록되어있는 것이다. (이 부분은 다소 확신이 서지않긴합니다.. 틀린부분이면 지적해주세요!)

클래스에서 이벤트 핸들러를 등록하기 위해 사용하는 이벤트 핸들러는 클래스 메소드로 만드는 것이 일반적인 패턴이다.
클릭하면 토글로 state가 변경되는 컴포넌트를 만들어보자.

import React from "react";
import "./styles.css";

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isOn: true };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState({ isOn: this.state.isOn ? false : true });
  }
  render() {
    return (
      <button onClick={this.handleClick}>{this.state.isOn ? "켜짐" : "꺼짐"}</button>
    );
  }
}

여기서 미리 알아야할것은 JS에서 함수를 콜백함수로 전달하면 전달된 함수 즉, 콜백함수는 this의 컨텍스트 대상을 잃게 된다.
즉, this.handleClick함수를 onClick에 전달(콜백으로 전달한거임)하면 this는 컨텍스트 대상을 잃게되어 원래는 this가 클래스를 가르켜야하는데(this는 가르키는 컨텍스트, 여기선 App이라는 클래스) this가 가르키는 대상을 잃게되어 thisundefined를 가르키게되어서 아무리 클릭을 눌러서 handleClick함수가 실행이되질않는다.
이럴땐 this.handleClick=this.handleClick.bind(this)this를 바인딩 해준다.
그러면 문제가 해결됨.

그치만 이 방법이 불편하면 arrow function을 사용할수 있다.
1. arrow function을 사용하는것도 클래스 메소드를 arrow function으로 정의하는 다소 실험적인 문법인 퍼블릭 클래스 필드 문법을 사용할수도 있고

변경한 부분

  handleClick = () => {
    this.setState({ isOn: this.state.isOn ? false : true });
  };
  1. onClick에 전달되는 형태를 arrow fuction으로 전달해서 this binding이슈를 해결할수 있다.

변경한 부분

  render() {
    return (
      <button
        onClick={() => {
          this.handleClick();
        }}
      >
        {this.state.isOn ? "켜짐" : "꺼짐"}
      </button>
    );
  }
  1. 의 방법으로 해결하면 클릭할때 (클릭하면 setState => 컴포넌트 리렌더링) 마다 this.handleClick()을 실행하는 익명의 함수를 계속 만들기에 좋지가 않다.

그렇기에 1의 방법을 사용하던가 생성자안에서 binding해서 해결하도록 하자.
(React도그렇게 추천중)

지금까지 한거 정리하자면

  • React에서 이벤트를 처리하는 방법으론 DOM Element에 oneline으로 이벤트를 처리하는 방법과 매우 흡사하지만 몇가지 차이점이 있다. (이벤트이름 camelCase, 이벤트 리스너를 함수로 전달, 브라우저의 고유동작 막는건 preventDefault()함수를 통해서, event객체는 합성이벤트(고유 이벤트완다름))
  • 클래스에서 등록하는 이벤트 리스너는 클래스 메소드를 통해서 등록하고 this binding issue가 존재하므로 여러가지 해결법으로 해결가능

위 내용은 React 공식문서를 보고 공부한 내용입니다.
https://ko.reactjs.org/docs/handling-events.html

0개의 댓글