들어가기 전에
- 이벤트 리스너: 이벤트를 처리하는 함수
- 이벤트 핸들러 속성명은 카멜케이스로만 작성 (HTML에서는 onclick, 리액트는 onClick)
-
이벤트 핸들러에는 필히 함수를 지정 (HTML에서는 문자열로 코드를 지정)
// HTML과 리액트에서 이벤트 리스너를 다룰 때 다른 점 <div onclick="console.log('Hello');"></div> <div onClick={console.log('Hello')}></div>
-
DOM 요소에만 이벤트 지원
- 커스텀 리액트 컴포넌트에서는 HTML 이벤트를 지원하지 않음
- 하지만 내부 Element에 DOM 요소를 담아 핸들러를 지정할 수 있음. 아래 예시 참고
// App.js
import React from 'react';
import PropTypes from "prop-types";
import Counter from "Counter";
import 'App.css';
class App extends React.Component {
render() {
return (
<Counter onClick={ () => console.log("Clicked")} />
);
}
}
export default App;
// Counter.js
import React from "react";
import PropTypes from "prop-types";
class Counter extends React.Component {
static propTypes = {
onClick: PropTypes.func,
}
render() {
return (
// 내부 element에 DOM 요소를 담아 핸들러 지정 가능
<div onClick={this.props.onClick}>
Counter
</div>
)
}
}
export default Counter;
이벤트 핸들러와 bind
아래 세 가지 케이스에서
handleChangeInput1
함수의 this는 e.target을 가리킨다.handleChangeInput2
함수의 this는 현재 컴포넌트를 가리킨다.handleChangeInput3
함수의 this도 현재 컴포넌트를 가리킨다.
class App extends React.Component {
constructor(props) {
super(props);
this.handleChangeInput2 = this.handleChangeInput2.bind(this);
}
handleChangeInput1 = function(e) {
const { name, value } = e.target;
console.log(`[handleChangeInput1] name=${name}, value=${value} <= this=`, this);
}
handleChangeInput2 = function(e) {
const { name, value } = e.target;
console.log(`[handleChangeInput2] name=${name}, value=${value} <= this=`, this);
}
handleChangeInput3 = (e) => {
const { name, value } = e.target;
console.log(`[handleChangeInput2] name=${name}, value=${value} <= this=`, this);
}
render () {
return (
<input name={"myquery"} onChange={this.handleChangeInput2} />
)
}
}
왜 그럴까?
function을 사용하면서 this가 해당 함수를 가리키도록 바뀌는데, 다시 현재 컴포넌트를 가리키도록 하려면 bind(this)
로 바꿔줘야 한다.
매번 bind로 바꿔주기 귀찮으니 화살표 함수를 사용하는 것이 편하다. ECMA 표준은 아니라 babel 플러그인이 필요하지만, create-react-app
으로 리액트 프로젝트를 생성하면 다른 설정 없이 바로 사용할 수 있다.
정리하자면,
handleChangeInput1
함수는 bind 작업을 해주지 않았기 때문에 this는 e.target을 가리킨다.handleChangeInput2
함수의 bind 작업을 해주었으므로 this는 현재 컴포넌트를 가리킨다.handleChangeInput3
이 화살표 함수를 사용했기 때문에 bind 작업 없어도 this가 현재 컴포넌트를 가리킨다.
화살표 함수 사용 예시
// App.js
import React from 'react';
import PropTypes from "prop-types";
import Counter from "Counter";
import 'App.css';
class App extends React.Component {
onChange = (e) => {
const { value } = e.target;
console.log('changed value: ', value, this);
} // 화살표 함수 사용 예시 (this가 컴포넌트를 가리킴)
render() {
return (
<div>
<Counter onClick={ () => console.log("Clicked")} />
<input onChange={this.onChange} />
</div>
/*
리액트 element는 항상 컴포넌트 1개만 반환해야 하기 때문에
div 같은 것으로 묶어주지 않으면 에러남
div로 묶기 싫으면 아래처럼 <React.Fragment>로 묶어도 됨
혹은 <></> 빈 태그로 묶어도 됨
<React.Fragment>
<Counter onClick={ () => console.log("Clicked")} />
<input />
</React.Fragment>
*/
);
}
}
export default App;
input 입력 문자열을 상태값으로 저장하기
// App.js
import React from 'react';
import PropTypes from "prop-types";
import Counter from "Counter";
import 'App.css';
class App extends React.Component {
state = {
myquery: "",
}
onChange = (e) => {
const { name, value } = e.target;
this.setState({
[name]: value, // name 변수에 담긴 문자열이 key 이름이 됨
})
}
render() {
return (
<div>
<input name="myquery" onChange={this.onChange} />
</div>
);
}
}
export default App;
이벤트 리스너 재활용
같은 onChange 이벤트 리스터를 다른 요소에서도 사용할 수 있다.
import React from 'react';
import PropTypes from "prop-types";
import Counter from "Counter";
import 'App.css';
class App extends React.Component {
state = {
myquery: "",
language: "",
}
// 만들어준 이벤트 리스너를 범용적으로 사용 가능
onChange = (e) => {
const { name, value } = e.target;
this.setState({
[name]: value,
})
}
render() {
return (
<div>
<input name="myquery" onChange={this.onChange} />
<input name="language" onChange={this.onChange} />
</div>
);
}
}
export default App;
번외) console.log말고 다른 방법으로 디버깅 하기
// App.js
import React from 'react';
import PropTypes from "prop-types";
import Counter from "Counter";
import 'App.css';
class App extends React.Component {
state = {
myquery: "",
language: "",
}
onChange = (e) => {
const { name, value } = e.target;
this.setState({
[name]: value,
})
}
render() {
return (
<div>
<input name="myquery" onChange={this.onChange} />
<input name="language" onChange={this.onChange} />
<hr />
{JSON.stringify(this.state)}
</div> // console.log말고도 stringify로 상태값 직렬화해서 디버깅용으로 확인할 수 있음
);
}
}
export default App;
JSON 직렬화해 객체를 실시간으로 확인하면서 디버깅할 수 있다.