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

Lynn·2021년 7월 29일
0

React

목록 보기
7/17
post-thumbnail

리액트의 이벤트 시스템

사용자가 웹 브라우저에서 DOM 요소들과 상호작용하는 것을 이벤트라고 한다. 예를 들어 버튼에 커서를 올렸을 때는 onmouseover 이벤트, 클릭했을 때는 onclick, 폼 요소는 값일 바뀔 때 onchange 이벤트를 실행한다. 이벤트 종류는 여기 참고

<button onClick={실행 함수명}>버튼</button>

이벤트를 사용할 때 주의 사항

  1. 이벤트 이름은 카멜 표기법으로 작성한다.
  2. 이벤트에 실행할 자바스크립트 코드를 전달하는 것이 아니라, 함수 형태의 값을 전달한다. (바로 화살표함수를 만들어서 전달해도 됨)
  3. DOM 요소에만 이벤트를 설정할 수 있다.
    즉, div, button, input, form, span 등의 요소가 아닌 직접 만든 컴포넌트에는 이벤트를 자체적으로 설정할 수 없다. 예를 들어, MyComponentonClick값을 설정한다면 onClickprops가 전달될 뿐이다. 하지만 이를 컴포넌트 내부에서 전달받아 DOM 이벤트로 설정하면 된다.
<MyComponent onClick={doSomethig}/>
// MyComponent 내부
<div onClick={this.props.onClick}>
	{/*...*/}
</div>

이벤트 핸들링 예제

onChange

import React, { 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;

코드를 저장하고 웹브라우저에서 크롬 개발자 도구를 열어 인풋에 아무것이나 입력하면 이벤트 객체console.log 되는 것을 확인할 수 있다. 여기서 이벤트 객체는 SyntheticEvent로 웹 브라우저의 네이티브 이벤트를 감싸는 객체이다.
SyntheticEvent는 네이티브 이벤트와 달리 이벤트가 끝나고 나면 이벤트가 초기화되므로 정보를 참조할 수 없다. 예를 들어, 0.5초 뒤에 e 객체를 참조하면 e 객체 내부의 모든 값이 비워지게 된다.
만약 비동기적으로 이벤트 객체를 참조할 일이 있다면, e.persist() 함수를 호출해 주어야 한다. 예를 들어 onChange 이벤트가 발생할 때, 앞으로 변할 인풋 값인 e.target.value를 콘솔에 기록하면 된다. 아래처럼 코드를 수정하면 값이 바뀜에 따라 콘솔에 바로바로 기록되는 것을 확인할 수 있다.

          onChange={
            (e) => {
              console.log(e.target.value);
            }
          }

state에 input 값 넣기

이번에는 state에 input 값을 넣어 볼 것이다. 아래 코드와 같이 이벤트 핸들링 함수 내부에서 this.setState 메서드를 호출하여 state를 업데이트한다. 그 다음에는 inputvalue 값을 state에 있는 값으로 설정하면 된다.
정말로 입력한 값이 state에 잘 들어갔는지, 그리고 input에서 그 값을 제대로 반영하는지 검증하기 위해 input 요소 코드 아래쪽에 button을 하나 만들고, 클릭 이벤트가 발생하면 현재 comment 값을 메시지 박스로 띄운 후 comment 값을 공백으로 설정했다.

import React, { 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);
            this.setState({
              message:''
            });
          }
        }>확인</button>
      </div>
    );
  }
}

export default EventPractice;

임의 메소드 만들기

위 코드는 임의 메소드를 정의함으로써 가독성을 높일 수 있다. 기본 방식으로 한다면 constructor 함수 내에서 바인딩이 이루어져야 하지만 바벨의 transform-class-properties 문법을 사용하여 화살표 함수 형태로 메서드를 정의하면 더 간단히 구현할 수 있다. 웬만하면 이벤트 핸들링 메소드는 handle~ 형식으로 네이밍 한다.

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>이벤트 연습</h1>
        <input
          type="text"
          name="message"
          placeholder="아무거나 입력해 보세요"
          value={this.state.message}
          onChange={this.handleChange}
        />
        <button onClick={this.handleClick}>확인</button>
      </div>
    );
  }
}
 
export default EventPractice;

input 여러 개 다루기

input이 여러 개일 때는 메소드를 여러 개 만들면 될까? 그것보다 효율적인 방법이 있겠지... 바로 event 객체를 활용하는 것이다. 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>이벤트 연습</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 값으로 사용한다는 뜻이다.

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

onKeyPress

Enter를 눌렀을 때 버튼을 클릭한 것과 같은 이벤트를 주기 위해 아래의 코드를 추가해 주고, input 태그에 onKeyPress={this.handleKeyPress} 속성을 준다.

handleKeyPress = (e) => {
  if (e.key === 'Enter') {
    this.handleClick();
  }
}

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

지금까지 작성한 코드를 함수형 컴포넌트로 바꿔 보자. 그러려면 useState를 써야겠지??

import React, { useState } from 'react';

const EventPractice = () => {
  
  const [form, setForm] = useState({
    username: '',
    message: ''
  });
  
  const { username, messgae } = 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;
profile
wanderlust

0개의 댓글