이벤트란 사용자가 웹 브라우저에서 DOM 요소들과 상호 작용하는 것을 말합니다.
이벤트의 종류는 위에 있는 종류말고도 많습니다. 더 보고싶다면 https://facebook.github.io/react/docs/event.html을 참고하면 됩니다.
4.2.1.1 컴포넌트 생성
// EventPractice.js
import { Component } from 'react';
class EventPractice extends Component {
render() {
return (
<div>
<h1>이벤트 연습</h1>
</div>
);
}
}
export default EventPractice;
4.2.1.2 App.js에서 EventPractice 렌더링
App 컴포넌트에서 EventPractice를 불러와 렌더링 합니다.
// App.js
import EventPractice from './EventPractice';
const App = () => {
return <EvnetPractice />;
};
export default App;
4.2.2.1 onChange 이벤트 설정
EventPractice 컴포넌트에 input 요소를 렌더링 하는 코드와 해당 요소에 onChange 이벤트를 설정하는 코드를 작성합니다.
// EventPractice.js
import { Component } from 'react';
class EventPractice extends Component {
render() {
return (
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="message"
placeholder="아무거나 입력해 보세요"
onChange={
(e) => {
console.log(e);
}
}
/>
</div>);
}
}
export default EventPractice;
여기서 콘솔에 기록되는 e 객체는 SyntheticEvent로 웹 브라우저의 네이티브 이벤트를 감싸는 객체입니다. SyntheticEvent는 네이티브 이벤트와 달리 이벤트가 끝나고 나면 이벤트가 초기화되므로 정보를 참조할 수 없습니다. 예를 들어, 0.5초 뒤에 e 객체를 참조하면 e 객체 내부의 모든 값이 비워지게 됩니다. 만약 비동기적으로 이벤트 객체를 참조할 일이 있다면 e.persist() 함수를 호출해 주어야 합니다.
4.2.2.2 state에 input 값 담기
이번에는 3장에서 배운 state에 input 값을 담아 보겠습니다. constructor에서 state 초깃값을 설정하고, 이벤트 핸들링 함수 내부에서 this.setState 메서드를 호출해 state를 업데이트해 봅시다. 그다음에는 input의 value 값을 state에 있는 값으로 설정하세요.
// EventPractice.js
import { Component } from 'react';
class EventPractice extends Component {
state = {
message: ''
}
render() {
return (
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="message"
placeholder="아무거나 입력해 보세요"
value={this.state.message}
onChange={
(e) => {
this.setState({
message: e.target.value
})
}
}
/>
</div>);
}
}
export default EventPractice;
4.2.2.3 버튼을 누를 때 comment 값을 공백으로 설정
입력한 값이 state에 잘 들어갔는지, 인풋에서 그 값을 제대로 반영하는지 보기 위해 button을 하나 만들고, 클릭 이벤트가 발생하면 현재 comment 값을 메시지 박스로 띄운 후 comment 값을 공백으로 설정하겠습니다.
// EventPractice.js
import { Component } from 'react';
clss EventPractice extends Component {
state = {
message: ''
}
render() {
return (
<div>
<h1>이벤트 연습</h1>
<input
(...)
/>
<button onClick={
() => {
alert(this.state.message);
this.setState({
message: ''
});
}
}>확인</button>
</div>);}
}
export default EventPractice;
4.1.1 절의 주의 사항에서 "이벤트에 실행할 자바스크립트 코드를 전달하는 것이 아니라, 함수 형태의 값을 전달합니다"라고 배웠습니다. 그렇기에 이벤트를 처리할 때 렌더링을 하는 동시에 함수를 만들어서 전달해 주었습니다. 이 방법 대신 함수를 미리 준비하여 전달하는 방법도 있습니다.
onChange와 onClick에 전달한 함수를 따로 빼내서 컴포넌트 임의 메서드를 만들어 보겠습니다.
// EventPractice.js
import { Component } from 'react';
clss EventPractice extends Component {
state = {
message: ''
}
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleChange(e) {
this.setState({
message: e.target.value
});}
handleClick() {
alert(this.state.message);
this.setState({
message: ''
});
}
render() {
return (
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="message"
placeholder="아무거나 입력해 보세요"
value={this.state.message}
onChange={this.handleChange}
/>
<button onClick={this.handleClick}>확인</button>
</div>);}
}
export default EventPractice;
함수가 호출될 때 this는 호출부에 따라 결정되므로, 클래스의 임의 메서드가 특정 HTML 요소의 이벤트로 등록되는 과정에서 메서드와 this의 관계가 끊어져 버립니다. 이 때문에 임의 메서드가 이벤트로 등록되어도 this를 컴포넌트 자신으로 제대로 가리키기 위해서는 메서드를 this와 바인딩하는 작업이 필요합니다. 만약 바인딩하지 않는 경우라면 this가 undefined를 가리키게 됩니다. 위 코드에서는 constructor 함수에서 함수를 바인딩하는 작업이 이루어지고 있습니다.
4.2.3.2 Property Initializer Syntax를 사용한 메서드 작성
위의 작업은 새 메서드를 만들 때마다 constructor를 수정해야 하기 때문에 불편하다고 느낄 수 있습니다. 이 작업을 간단하게 하려면 바벨의 transform-class-properties 문법을 사용해 화살표 함수 형태로 메서드를 정의합니다.
// EventPractice.js
import { Component } from 'react';
clss EventPractice extends Component {
state = {
message: ''
}
handleChange = (e) => {
this.setState({
message: e.target.value
});}
handleClick = () => {
alert(this.state.message);
this.setState({
message: ''
});
}
render() {
return (
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="message"
placeholder="아무거나 입력해 보세요"
value={this.state.message}
onChange={this.handleChange}
/>
<button onClick={this.handleClick}>확인</button>
</div>);}
}
export default EventPractice;
input이 여러 개일 때는 어떻게 작업할까요? event 객체를 활용하면 됩니다. e.target.name의 값을 사용하면 됩니다. onChange 이벤트 핸들러에서 e.target.name은 해당 인풋의 name을 가리킵니다. 위 코드에서는 message입니다. 다음 코드에서는 render 함수에서 name 값이 username인 input을 렌더링 해줬고, state 쪽에도 username이라는 값을 추가해 주었습니다. 그리고 handleChange도 변경했습니다.
// EventPractice.js
import { Component } from 'react';
clss EventPractice extends Component {
state = {
username: '',
message: ''
}
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value
});}
handleClick = () => {
alert(this.state.username + ": " + this.state.message);
this.setState({
username: '',
message: ''
});
}
render() {
return (
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="username"
placeholder="사용자명"
value={this.state.username}
onChange={this.handleChange}
/>
<input
type="text"
name="message"
placeholder="아무거나 입력해 보세요"
value={this.state.message}
onChange={this.handleChange}
/>
<button onClick={this.handleClick}>확인</button>
</div>);}
}
export default EventPractice;
여기서는 다음 코드가 핵심입니다.
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value
});}
객체 안에서 key를 []로 감싸면 그 안에 넣은 레퍼런스가 가리키는 실제 값이 key 값으로 사용됩니다.
이번에는 키를 눌렀을 때 발생하는 keyPress 이벤트를 처리하는 방법을 알아보겠습니다. comment 인풋에서 Enter를 눌렀을 때 handleClick 메서드를 호출하도록 코드를 작성해 봅시다.
// EventPractice.js
import { Component } from 'react';
clss EventPractice extends Component {
(...)
handleKeyPress = (e) => {
if (e.key === 'Enter') {
this.handleClick();
}
}
render() {
return (
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="username"
placeholder="사용자명"
value={this.state.username}
onChange={this.handleChange}
/>
<input
type="text"
name="message"
placeholder="아무거나 입력해 보세요"
value={this.state.message}
onChange={this.handleChange}
onKeyPress={this.handleKeyPress}
/>
<button onClick={this.handleClick}>확인</button>
</div>);}
}
export default EventPractice;
import { useState } from 'react';
const EventPractice = () => {
const [username, setUsername] = useState('');
const [message, setMessage] = useState('');
const onChangeUsername = e => setUsername(e.target.value);
const onChangeMessage = e => setMessage(e.target.value);
const onClick = () => {
alert(username + ": " + message);
setUsername('');
setMessage('');
};
const onKeyPress = e => {
if (e.key === 'Enter') {
onClick();
}
};
return (
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="username"
placeholder="사용자명"
value={username}
onChange={onChangeUsername}
/>
<input
type="text"
name="message"
placeholder="아무거나 입력해 보세요"
value={message}
onChange={onChangeMessage}
onKeyPress={onKeyPress}
/>
<button onClick={onClick}>확인</button>
</div>
);
};
export default EventPractice;
위 코드에서는 e.target.name을 활용하지 않고 onChange 관련 함수 두 개를 따로 만들었습니다. 하지만 인풋의 개수가 많아질 것 같으면 e.target.name을 활용하는 것이 더 좋을 수도 있습니다.
이번에는 useState를 통해 사용하는 상태에 문자열이 아닌 객체를 넣어 보겠습니다.
import { useState } from 'react';
const EventPractice = () => {
const [form, setForm] = useState({
username: '',
message: ''
});
const { username, message } = form;
const onChange = e => {
const nextForm = {
...form,
[e.target.name]: e.target.value
};
setForm(nextForm);
};
const onClick = () => {
alert(username + ": " + message);
setForm({
username: '',
message: ''
});
};
const onKeyPress = e => {
if (e.key === 'Enter') {
onClick();
}
};
return (
<div>
<h1>이벤트 연습</h1>
<input
type="text"
name="username"
placeholder="사용자명"
value={username}
onChange={onChange}
/>
<input
type="text"
name="message"
placeholder="아무거나 입력해 보세요"
value={message}
onChange={onChange}
onKeyPress={onKeyPress}
/>
<button onClick={onClick}>확인</button>
</div>
);
};
export default EventPractice;
e.target.name 값을 활용하려면, 위와 같이 useState를 쓸 때 인풋 값들이 들어 있는 form 객체를 사용해 주면 됩니다.
정답
1. 함수 형태로 전달
2. 바인딩
3. []
Corner React Starter #1
Editor 알밤
[리액트를 다루는 기술] 6장 컴포넌트 반복 (0) | 2021.11.29 |
---|---|
[리액트를 다루는 기술] 5장 ref: DOM에 이름 달기 (0) | 2021.11.22 |
[리액트를 다루는 기술] 3장 컴포넌트 (0) | 2021.11.08 |
[리액트를 다루는 기술] 2장 JSX (0) | 2021.11.01 |
리액트를 다루는 기술 - 1장. 리액트 시작 (0) | 2021.11.01 |