안녕하세요. 이번 시간에는 React 컴포넌트에 이벤트를 연결(바인딩)하는 방법을 알아보겠습니다. hook을 배우려고 하시는 분들은 이 강좌 대신 useCallback 강좌 로 넘어가세요. 물론 저는 class 컴포넌트 문법도 알아두시는 것을 권장드립니다.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class Basic extends Component {
constructor(props) {
super(props);
this.state = {
hidden: false,
};
this.onClickButton = this.onClickButton.bind(this);
}
onClickButton() {
this.setState(() => ({ hidden: true }));
}
render() {
return (
<div>
<span>저는 {this.props.lang} 전문 {this.props.name}입니다!</span>
{!this.state.hidden && <span>{this.props.birth}년에 태어났습니다.</span>}
<button onClick={this.onClickButton}>숨기기</button>
</div>
);
}
}
Basic.propTypes = {
name: PropTypes.string.isRequired,
birth: PropTypes.number.isRequired,
lang: PropTypes.string,
};
Basic.defaultProps = {
lang: 'Javascript',
};
export default Basic;
지난 시간에 봤던 컴포넌트죠? 그런데 한 가지 달라진 부분이 있습니다. button의 onClick 속성을 따로 onClickButton 메소드로 분리했는데요. 그게 전부가 아닙니다. ES2015 class에서만 있는 특별한 현상이 있습니다.
바로 constructor 안에 this.onClickButton = this.onClickButton.bind(this);
라는 이상한 코드가 들어있습니다. 이 부분이 이벤트를 바인드(bind)하는 부분입니다. 저는 처음에 도대체 this.onClickButton
에 this를 왜 bind하는 건지 이해가 되지 않았습니다. 그런데 함수의 원리를 알고 보면 이해가 됩니다.
onClickButton() {
this.setState(() => ({ hidden: true }));
}
이 부분은 ES5 코드로 보면
onClickButton: function() {
this.setState(() => ({ hidden: true }));
}
인데요. 문제점은 this.onClickButton = this.onClickButton.bind(this);
를 하지 않으면 render 메소드에서 this.onClickButton
함수를 호출했을 때 함수의 this가 window나 undefined가 되어버립니다. 따라서 window나 undefined에는 setState 메소드가 없기 때문에 this.setState
호출 시 에러가 발생하죠.
왜 this가 window나 undefined가 되는지 알기 위해서는, this.onClickButton
이 실행되는 환경을 잘 보아야 합니다. this.onClickButton
이 실행되는 순간 (function() { this.setState(() => ({ hidden: true })); })();
가 실행되는데, 이때의 this는 window입니다. 이 함수 자체만 놓고 보면, 자바스크립트 엔진은 this가 뭔지 모르기 때문에 그냥 최상위와 연결시켜버립니다.
이해가 잘 안 되나요? 그렇다면 이런 복잡한 생각 없이 쉽게 하는 방법이 있습니다. ES2015의 새로운 기능인 arrow function을 사용하면 됩니다. arrow function은 this를 자동으로 bind해주기 때문에 간단합니다. 대신 @babel/plugin-proposal-class-properties 가 필요합니다. 위의 컴포넌트는 다음과 같아집니다.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class Basic extends Component {
static propTypes = {
name: PropTypes.string.isRequired,
birth: PropTypes.number.isRequired,
lang: PropTypes.string,
};
static defaultProps = {
lang: 'Javascript',
};
state = {
hidden: false,
};
onClickButton = () => {
this.setState(() => ({ hidden: true }));
}
render() {
return (
<div>
<span>저는 {this.props.lang} 전문 {this.props.name}입니다!</span>
{!this.state.hidden && <span>{this.props.birth}년에 태어났습니다.</span>}
<button onClick={this.onClickButton}>숨기기</button>
</div>
);
}
}
static 키워드 덕분에 Basic 컴포넌트와 따로 분리되어 있던 defaultProps와 propTypes가 안으로 들어갔습니다. 또 constructor 안에 있던 state는 바깥으로 나왔고요. onClickButton 메소드까지 arrow function으로 만들면 이제 constructor가 필요없어집니다. 복잡한게 싫다 싶으면 이 방법을 사용합니다. 앞으로도 이 방법을 사용하겠습니다.
다음 시간에는 컨텍스트(context)와 참조(Reference)에 대해 알아보겠습니다!