본문 바로가기
JavaScript/React

React) 이벤트 핸들링 (클래스형 컴포넌트, 함수형 컴포넌트)

by 박채니 2022. 11. 10.
안녕하세요, 코린이의 코딩 학습기 채니 입니다.
[리액트를 다루는 기술]의 책을 참고하여 포스팅한 개인 공부 내용입니다.

 

이벤트 핸들링

 

이벤트 사용 시 주의 사항

 

① 이벤트 이름은 카멜 표기법으로 작성

    - oncick → onClick 혹은 onkeyup → onKeyUp

 

② 이벤트에 실행할 자바스크립트 코드 전달이 아닌 함수 형태의 값을 전달

     - 화살표 함수 문법으로 함수를 생성하여 전달 혹은 외부에 미리 만들어서 전달

 

DOM 요소에만 이벤트 설정 가능

     - div, button, input, form, span 등의 DOM 요소에는 이벤트 설정 가능하지만, 만든 컴포넌트에는 설정 불가

     - 하지만, 전달받은 props 를 컴포넌트 내부의 DOM 이벤트로 설정은 가능 

 

이벤트 종류

 

  •  Clipboard
  • Touch
  • Composition
  • UI
  • Keyboard
  • Wheel
  • Focus
  • Media
  • Form
  • Image
  • Mouse
  • Animation
  • Selection
  • Transition

클래스형 컴포넌트로 이벤트 설정하기

 

컴포넌트 생성 및 불러오기

 

컴포넌트 생성

EventPractice.js

import { Component } from "react";

class EventPractice extends Component {
    render() {
        return (
            <div>
                <h1>이벤트 연습</h1>
            </div>
        );
    }
}

export default EventPractice;

 

App.js에서 EventPractice 렌더링

App.js

import './App.css'
import EventPractice from './EventPractice';

const App = () => {
  return <EventPractice />;
}

export default App;

 

onChange 이벤트 핸들링하기

 

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;

onChange 이벤트를 설정하였고, input에 값을 입력하니 이벤트 객체가 콘솔창에 나타난 것을 확인할 수 있습니다.

 

여기서 e 객체SyntheticEvent로 웹 브라우저의 네이티브 이벤트를 감싸는 객체입니다.

※ SyntheticEvent

 - 네이티브 이벤트와 달리 이벤트가 끝나면 이벤트가 초기화되므로 정보를 참조할 수 없음

 (0.5초 뒤에 e 객체를 참조하면 e 객체 내부의 모든 값이 비워지게 됨)

- 따라서 비동기적으로 이벤트 객체를 참조해야한다면 e.persist() 함수 호출

 

입력되는 값을 콘솔에 찍어보겠습니다.

import { Component } from "react";

class EventPractice extends Component {
    render() {
        return (
            <div>
                <h1>이벤트 연습</h1>
                <input 
                    type="text" 
                    name="message" 
                    placeholder="아무거나 입력하세요" 
                    onChange={(e) => {
                        console.log(e.target.value);
                    }
                }
            />
            </div>
        );
    }
}

export default EventPractice;

 

state에 input 값 담기

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;

change 이벤트가 발생할 때 state의 message에 e.target.value 값을 저장하고 이를 input의 value로 설정(this.state.message) 해주었습니다.

 

버튼을 누를 때 comment 값을 공백으로 설정

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
                        })
                    }
                }
            />
            <button onClick={
                () => {
                    alert(this.state.message); // state의 message 값 띄우기(state에 잘 들어갔는지 확인)
                    this.setState({
                        message: '' // 값 초기화
                    });
                }
            }>
                확인
            </button>
            </div>
        );
    }
}

export default EventPractice;

state에 값이 잘 들어갔고, 값을 제대로 반영하는 것을 확인할 수 있습니다.

현재 comment 값을 띄운 후 공백으로 재설정해주었습니다.

 

임의 메서드 만들기

 

지금까지 렌더링과 동시에 함수를 만들어 전달해주었는데, 별도로 분리하여 전달해보도록 하겠습니다.

 

기본 방식

import { 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>이벤트 연습</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에서 바인딩 작업을 해주었습니다.

 

Property Initializer Syntax를 사용한 메소드 작성

- 메소드 바인딩은 생성자 메소드에서 하는 것이 정석이지만, 다소 불편함이 있음

- 이를 해결하기 위해 바벨의 transform-class-properties 문법을 사용하여 화살표 함수 형태로 메소드를 정의

import { 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>이벤트 연습</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을 이용하여 state를 설정해 input 여러 개를 쉽게 다룰 수 있습니다.

import { Component } from "react";

class EventPractice extends Component {

    state = {
        message: '',
        username: ''
    }

    handleChange = (e) => {
        this.setState({
            [e.target.name]: e.target.value
        })
    }

    handleClick = () => {
        alert(this.state.username + " : " + this.state.message);
        this.setState({
            message: '',
            username: ''
        });
    }

    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;

this.target.name을 이용하여 state를 설정하여 쉽게 해결되었습니다.

 

    handleChange = (e) => {
        this.setState({
            [e.target.name]: e.target.value
        })
    }

객체 안에서 key를 [ ]로 감싸면 그 안에 넣은 레퍼런스가 가리키는 실제 값이 key 값으로 사용됩니다.

 

onKeyPress 이벤트 핸들링

 

import { Component } from "react";

class EventPractice extends Component {

    state = {
        message: '',
        username: ''
    }

    handleChange = (e) => {
        this.setState({
            [e.target.name]: e.target.value
        })
    }

    handleClick = () => {
        alert(this.state.username + " : " + this.state.message);
        this.setState({
            message: '',
            username: ''
        });
    }

    handlePress = (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.handlePress}
            />
            <button onClick={this.handleClick}>확인</button>
            </div>
        );
    }
}

export default EventPractice;

두 번째 input 박스에서 입력 후 Enter를 누르니 handleClick 메소드가 실행되어 alert 창이 띄워지는 것을 확인할 수 있습니다.

 


함수 컴포넌트로 구현해 보기

 

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 활용하지 않고 함수를 별도로 생성해주었습니다.

 

이번엔 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); // 생성한 form set!
    };

    const onClick = e => {
        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;

위처럼 활용할 수 있습니다.