리엑트의 이벤트 시스템
사용자가 웹 브라우저에서 DOM요소들과 상호 작용하는 것을 이벤트(event)라고 한다.
리액트의 이벤트 시스템은 웹 브라우저의 HTML 이벤트와 인터페이스가 동일하기 때문에 사용법이 꽤 비슷하다.
하지만 주의해야 할 몇 가지 사항이 있다.
이벤트 사용 시 주의사항
1. 이벤트 이름은 카멜표기법으로 작성
HTML에서는 onclick으로 작성하지만 리액트에서는 카멜 표기법으로 onClick으로 작성해야한다
2. 이벤트에는 함수 형태의 값을 전달
HTML에서 이벤트를 설정할 때 ""안에 실행할 코드를 넣었지만, 리액트에서는 함수 형태의 객체를 전달한다. 애로우(화 살표)함수 문법을 사용하거나 혹은 외부에 미리 함수를 만들어서 전달하기도한다.
3. DOM요소에만 이벤트를 설정할 수 있다.
div, button, input, form, span 등의 DOM요소에는 이벤트를 설정할 수 있지만, 직접만든 리액트 컴포넌트에는 이벤트를 자체적으로 설정할 수 없다.
<MyComponent onClick={doSomethine}>
이 코드는 이름이 onClick인 props를 컴포넌트에 전달할 뿐이다.
컴포넌트에 자체적으로 이벤트를 설정할 수는 없지만, 전달받은 props를 컴포넌트 내부의 DOM이벤트로
설정할 수는있다.
<div onClick={this.props.onClick}>{/* ... */}</div>
리액트에서 지원하는 다양한 이벤트 종류는 아래의 링크를 통해 확인할 수 있다.
이벤트 핸들리 예제
컴포넌트 생성 및 불러오기
src 디렉토리에 EventPractice.js 파일을 만들고 App 컴포넌트에서 불러와 렌더링한다.
EventPractice.js
import React, { Component } from 'react';
class eventPractice extends Component {
render() {
return (
<div>
<h1>Event Practice</h1>
</div>
);
}
}
export default eventPractice; <h1>Event Practice</h1>
App.js
import React from 'react';
import EventPractice from './eventPractice';
const App = () => {
return <EventPractice/>
}
export default App;
onChange 이벤트 핸들링하기
EventPractice 컴포넌트에 input 요소를 렌더링하는 코드와 해당 요소에 onChange 이벤트를 설정하는 코드를 작성한다.
import React, { Component } from 'react';
class eventPractice extends Component {
render() {
return (
<div>
<h1>Event Practice</h1>
<input
tpye="text"
name="message"
placeholder="type something"
onChange={e =>{
console.log(e);
}}
/>
</div>
);
}
}
export default eventPractice;
React 실행 후 콘솔창을 킨 후 input요소에 텍스트를 입력해본다.
콘솔에 SyntheticzEvent 라는 객체가 나타나는 것을 볼 수 있는데, 이 객체는 웹 브라우저의 네이티브 이벤트를 감싸는 객체로, 이벤트 함수의 파라미터(예제 코드에서는 e)로 접근할 수 있다.
네이티브 이벤트와 인터페이스가 같으므로, 순수 JS에서 HTML이벤트를 다룰 때와 똑같이 사용하면되지만, 네이티브 이벤트와 달리 이벤트가 끝나면 이벤트가 초기화 되므로 정보를 참조할 수 없기 때문에ㅡ 비동기적으로 이벤트 객체를 참조해야 한다면 e.persist()함수를 호출해 주어야 한다
이제 state에 input값을 담아보도록 한다. state 초기값을 설정하고 이벤트 핸들링 함수 내부에서 this.setState 메소드를 호출하여 state를 업데이트 한다
import React, { Component } from 'react';
class eventPractice extends Component {
state = {
message:'',
}
render() {
return (
<div>
<h1>Event Practice</h1>
<input
tpye="text"
name="message"
placeholder="type something"
value={this.state.message}
onChange={e =>{
this.setState({
message: e.target.value,
})
}}
/>
<button
onClick={ () => {
alert(this.state.message);
this.setState({
message:'',
});
}}
>
submit
</button>
</div>
);
}
}
export default eventPractice;
input에 아무거나 입력 후 버튼을 누르면 해당 값이 alert으로 호출되는것을 확인 할 수 있다.
임의 메소드 만들기
현재의 코드는 이벤트에 실행할 함수를 렌더링 메소드 내부에 만들어서 전달해주고 있다. 이 방법 대신 함수를 미리 만들어 전달하면 가독성을 높일 수 있다.
import React, { Component } from 'react';
class 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>Event Practice</h1>
<input
type="text"
name="message"
placeholder="type something"
value={this.state.message}
onChange={this.handleChange}
/>
<button onClick={this.handleClick}>Submit</button>
</div>
);
}
}
export default EventPractice;
여기서 주의해야할 점은 this 바인딩이다. this는 함수가 어디서 호출되는지에 따라 결정된다.
클래스의 임의 메소드가 특정 HTML요소의 이벤트로 등록되는 과정에서 메도스와 this의 관계가 끊어지게된다.
따라서 this를 컴포넌트 자신으로 가르키게 하기 위해서는 메소드를 this와 바인딩하는 작업이 필요하고, 현재 코드에서는 constructor 함수 내부에서 함수를 바인딩하고 있다. 메소드 바인딩은 이렇게 생성자 메소드에서 하는 것이 정석이다. 새 메소드를 만들때마다 constuctor도 수정해야하기 때문이다.
하지만 클래스형 컴포넌트에서 public class fields 문법을 사용하여 화살표 함수로 메소드를 구현하면, 좀 더 편하게 작성할 수 있다. 화살표 함수의 this는 부모함수의 this를 상속받는데 JS에서 클래스는 함수로 구현되어 있기 때문에 this는 컴포넌트 자신이 되므로 bind(this)를 하지 않아도 된다.
import React, { Component } from 'react';
class 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>Event Practice</h1>
<input
type="text"
name="message"
placeholder="type something"
value={this.state.message}
onChange={this.handleChange}
/>
<button onClick={this.handleClick}>Submit</button>
</div>
);
}
}
export default EventPractice;
이 문법은 실험적인 문법으로 바벨을 설정해주어야 하는데 Create-React-App으로 만든 프로젝트는 이 문법이 기본적으로 설정되어 있으므로 사용 가능하다.
input 여러개 다루기
이제 input 값을 state에 넣는 방법을 알아보았다. input이 여러개일때 방법을 알아보자. 메소드를 여러개 만들어 해결할 수 있지만, event 객체를 활용하면 더 쉽게 처리할 수 있다
onChange 이벤트 핸들러에서 e.target.name은 해당 input의 name을 가리킨다. 이값을 이용해 state를 설정하면 더 쉽게 해결할 수 있다.
import React, { Component } from 'react';
class 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>Event Practice</h1>
<input
type="text"
name="username"
placeholder="User name"
value={this.state.username}
onChange={this.handleChange}
/>
<input
type="text"
name="message"
placeholder="type something"
value={this.state.message}
onChange={this.handleChange}
/>
<button onClick={this.handleClick}>Submit</button>
</div>
);
}
}
export default EventPractice;
handleChange = e => {
this.setState({
[e.target.name]: e.target.value,
});
};
이 코드를 보면 e.target.name을 []로 감싸는데 이렇게 객체안에서 key를 []로 감싸면 그 안에 넣은 레퍼런스가 가리키는 실제 값이 key값으로 사용된다.
onKeyPress 이벤트 핸들링
키를 눌렀을 때 발생하는 KeyPress 이벤트를 처리해본다. 두 번째 인풋에서 Enter키를 눌렀을때 handleCllck 메소드를 호출하도록 코드를 추가한다.
import React, { Component } from 'react';
class 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: '',
});
};
handleKeyPress = e => {
if (e.key === 'Enter') {
this.handleClick();
}
};
render() {
return (
<div>
<h1>Event Practice</h1>
<input
type="text"
name="username"
placeholder="User name"
value={this.state.username}
onChange={this.handleChange}
/>
<input
type="text"
name="message"
placeholder="type something"
value={this.state.message}
onChange={this.handleChange}
onKeyPress={this.handleKeyPress}
/>
<button onClick={this.handleClick}>Submit</button>
</div>
);
}
}
export default EventPractice;
함수형 컴포넌트로 구현해 보기
이제 클래스형 컴포넌트로 작성했던 EventPractice 컴포넌트를 함수형 컴포넌트로 바꾸어본다.
import React, { 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>Event Practice</h1>
<input
type="text"
name="username"
placeholder="User name"
value={username}
onChange={onChangeUsername}
/>
<input
type="text"
name="message"
placeholder="type something"
value={message}
onChange={onChangeMessage}
onKeyPress={onKeyPress}
/>
<button onClick={onClick}>Submit</button>
</div>
);
};
export default EventPractice;
기능이 이전과 같이 동작하는지 확인해본다.
위의 함수형 컴포넌트 코드에서는 e.target.name을 사용하지 않고 onChange 관련 함수를 따로 만들었다. 하지만 input의 개수가 많아질수록 불편해지므로 e.target.name을 활용하는 것이 더 좋을 수 있다.
import React, { 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>Event Practice</h1>
<input
type="text"
name="username"
placeholder="User name"
value={username}
onChange={onChange}
/>
<input
type="text"
name="message"
placeholder="type something"
value={message}
onChange={onChange}
onKeyPress={onKeyPress}
/>
<button onClick={onClick}>Submit</button>
</div>
);
};
export default EventPractice;
정리
리액트에서 이벤트를 다루는 것은 순수 JS또는 jQuery를 사용한 웹 애플리케이션에서 이벤트를 다루는 것과 비슷하다. 따라서 기존 HTML DOM Event를 알고 있다면 리액트의 컴포넌트 이벤트도 쉽게 다룰 수 있다.
참조
김민준님의 리액트를 다루는 기술 참조
'FrontEnd > React' 카테고리의 다른 글
리엑트 공부 재시작 ! (0) | 2023.01.22 |
---|---|
[React] Modal창 열기 (0) | 2023.01.22 |
React - Node 연동 (0) | 2023.01.22 |
[React] UseEffect (0) | 2023.01.01 |
ref: DOM에 이름달기 (0) | 2022.10.14 |