[리액트를 다루는 기술] 4장 이벤트 핸들링

devHagaa·2022년 6월 22일
post-thumbnail

이 포스팅은 김민준님의 '리액트를 다루는 기술'을 요약한 글입니다.

이벤트 : 웹브라우저에서 DOM 요소들과 상호작용 하는 것

4.1 리액트의 이벤트 시스템

이벤트 사용할 때 주의 사항
- 이벤트 이름은 카멜 표기법으로 작성 ex> onClick, onKeyUP
- 함수 형태의 값 전달 (화살표 함수, 렌더링 부분 외부)
- DOM 요소에만 이벤트 설정 (직접만든 컴포넌트에 이벤트를 자체적으로 설정 불가, 하지만 전달받은 props를 컴포넌트 내부의 DOM 이벤트로 설정은 가능

이벤트 종류
- Clipboard
- Touch
- Composition
- UI
- keyboard
- Wheel
- Focus
- Media
- Form
- Image
- Mouse
- Animation
- Selection
- Transition

4.2 예제로 이벤트 핸들링 익히기

4.2.2.2 state에 input 값 담기

// EventPractice.js

import { Component } from 'react';

class EventPractice extends Component{
    state={
        message:'' // state 초깃값 설정
    }

    render(){
        return(
            <div>
                <h1>이벤트 연습</h1>
                <input 
                    type="text"
                    name="message"
                    placeholder="아무거나 입력해 보세요"
                    value={this.state.message} // input의 value 값을 state에 있는 값으로 설정
                    onChange={
                		(e) => {
                  			this.setState({
                    			message: e.target.value // state를 업데이트
                  			})
                		}
                    }
                />
            	<button onClick={
					() => {
=						alert(this.state.message);
						this.setState({
							message: '' // comment 값을 공백으로 설정
						});
					}
				}>확인</button>
            </div>
        )
    }

}

export default EventPractice;

4.2.3 임의 메서드 만들기

함수가 호출될 때 this는 호출부에 따라 결정되므로, 클래스의 임의 메서드가 특정 HTML 요소의 이벤트로 등록되는 과정에서 메서드와 this의 관계가 끊어져 버립니다. 이 때문에 임의 메서드가 이벤트로 등록되어도 this를 컴포넌트 자신으로 제대로 가리키기 위해서는 메서드를 this아 바인딩하는 작업이 필요합니다. 만약 바인딩하지 않는 경우라면 this가 undefined를 가리키게 됩니다.

아래 코드를 보면 constructor 함수에서 함수를 바인딩하는 작업이 이루어지고 있습니다.

// EventPractice.js

import { Component } from 'react';

class EventPractice extends Component{
    state={
        message:'' // state 초깃값 설정
    }

		constructor(props) {
			super(props);
			this.handleChange = this.handleChange.bind(this);
			this.handleClick = this.handleClick.bind(this);
		}

		handleChange(e) {
				this.setState({
					message: e.target.value // 입력된 값으로 state값을 업데이트
				});
			}

		handleClick() {
			alert(this.state.message);
			this.setState({
				message: '' // state값을 공백으로 초기화
			});
		}

    render(){
        return(
            <div>
                <h1>이벤트 연습</h1>
                <input 
                    type="text"
                    name="message"
                    placeholder="아무거나 입력해 보세요"
                    value={this.state.message} // input의 value 값을 state에 있는 값으로 설정
                    onChange={this.handleChange} // 변경사항이 있으면 handleChange 이벤트 실행
                />
								<button onClick={this.handleClick}>확인</button> {/* 클릭 시 handleClick이벤트 실행 */}
            </div>
        )
    }

}

export default EventPractice;

4.2.3.2 Property Initializer Syntax를 사용한 메서드 작성

메서드 바인딩은 생성자 메서드에서 하는 것이 정석입니다.

새 메서드를 만들 때마다 constructor도 수정하는 것이 번거롭기 때문에 더 간단하게 하기 위해서 바벨의 transform-class-properties 문법을 사용하여 화살표 함수 형태로 메서드를 정의해 보겠습니다.

// EventPractice.js

import { Component } from 'react';

class EventPractice extends Component{
    state={
        message:'' // state 초깃값 설정
    }

		handleChange = (e) => {
				this.setState({
					message: e.target.value // 입력된 값으로 state값을 업데이트
				});
			}

		handleClick = () => {
			alert(this.state.message);
			this.setState({
				message: '' // state값을 공백으로 초기화
			});
		}

    render(){
        return(
            <div>
                <h1>이벤트 연습</h1>
                <input 
                    type="text"
                    name="message"
                    placeholder="아무거나 입력해 보세요"
                    value={this.state.message} // input의 value 값을 state에 있는 값으로 설정
                    onChange={this.handleChange} // 변경사항이 있으면 handleChange 이벤트 실행
                />
								<button onClick={this.handleClick}>확인</button> {/* 클릭 시 handleClick이벤트 실행 */}
            </div>
        )
    }

}

export default EventPractice;

4.2.4 input 여러 개 다루기

// EventPractice.js

import { 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>이벤트 연습</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;

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

예를 들어 다음과 같은 객체를 만들면

const name = 'variantKey';
const object = {
	[name] : 'value'
};

결과

{
	‘variantKey’ : ‘value’
}

4.2.5 onKeyPress 이벤트 핸들링

// EventPractice.js

import { 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>이벤트 연습</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;

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

// EventPractice.js

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;
// EventPractice.js
// 인풋값이 많아질 경우 활용하기 좋은 예제

import { useState } from 'react';

const EventPractice = () => {
    const [form, setForm] = useState({
        username:'',
        message:''
    });

    const {username, message} = form;
    const onChange = e => {
        const nextForm = {
            ...form,                         // 기존 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;

4.4 정리

  • 리액트에서 이벤트를 다루는 것은 순수 자바스크립트 또는 JQuery를 사용한 웹 애플리케이션에서 이벤트를 다루는 것과 비슷합니다.
  • 클래스형 컴포넌트로도 구현해 보고 함수형 컴포넌트로도 구현해 보았습니다.
  • 함수형 컴포넌트에서 여러 개의 인풋 상태를 관리하기 위해 useState에서 form 객체를 사용
  • 8장에서 배울 useReducer와 커스텀 Hooks를 사용하면 이 작업을 훨씬 편하게 할 수 있습니다.
profile
디자이너인가 퍼블리셔인가 프론트엔드개발자인가 정체성의 혼란을 겪는 개린이

0개의 댓글