[React] Event Handling

youngminss·2021년 8월 27일
0

React

목록 보기
4/7

개요

사용자가 웹 브라우저에서 DOM 요소들과 상호 작업하는 것을 이벤트(Event) 라고 한다.
리액트에서는 순수 HTML - Javascript 의 이벤트 핸들링 방식과 조금은 다른 방식으로 동작한다.
리액트에서의 Event Handling 에 대해 살펴본다.

본론

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

이벤트 이름은 카멜케이스(camelCase) 작성한다.

  • 예를들어 onclick 이벤트는 리액트에서 onClick 으로, onchange 이벤트는 onChange 형태로 작성한다.

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

  • 리액트에서는 함수 형태의 객체를 전달한다.
  • 보통 화살표함수 를 통해 함수를 만들어 바로 전달하거나, 렌더링 부분 외부에서 미리 만들어서 전달 하기도 한다.

DOM 요소에만 이벤트를 설정할 수 있다.

  • 즉, div, button, input, form 등의 DOM 요소에는 이벤트를 설정할 수 있다.
  • 직접 만든 컴포넌트에는 이벤트를 자체적으로 설정할 수 없다.
  • 단지, 컴포넌트에서 이벤트를 작성한 것은 컴포넌트 내부에서 props 를 통해 전달할 수 있고, 내부에서 전달받은 이벤트를 사용할 수 있다.

이벤트 종류

리액트에서 지원하는 이벤트 종류 리액트 공식 문서

리액트에서 Event 객체는 SyntheticEvent 이다.

  • SyntheticEvent 는 네이티브 이벤트와 달리, 이벤트가 끝나고 나면 이벤트가 초기화 되므로 이후에 정보를 참조할 수 없다.
  • 만약, 비동기적으로 이벤트 객체를 참조할 일이 있다면, event.persist( ) 를 호출 해준다.

임의 메서드 만들기

리액트 이벤트 처리 주의사항에서 자바스크립트 코드를 전달하는 것이 아니라, 함수 형태 의 값을 전달한다. 라고 했다.

  • 함수 형태를 전달해도 되고, 함수를 미리 준비하여 전달하는 방법이 둘의 성능상 차이는 거의 없고, 단지 가독성이 훨씬 높아진다.
// 클래스 컴포넌트
import React, { Component } from "react";

export default class EventPractice extends Component {
  
  // constructor(생성자) 함수에서 각 함수를 이 클래스 컴포넌트에 binding(바인딩) 작성이 필요
  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(e) {
    alert(this.state.message);
    this.setState({
      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="Email 을 입력하세요."
    value={this.state.message}
    onChange={this.handleChange}
  />
  <button onClick={this.handleClick}>확인</button>
</div>
    );
  }
}
  • 클래스 컴포넌트 에서는 this. 방식으로 접근을 많이하는데, this호출부에 따라 결정되므로, 클래스의 임의 메서드가 특정 HTML 요소의 이벤트로 등록하는 과정에서 메서드와 this 의 관계가 끊어져 버린다

  • 이 때문에 임의 메서드가 이벤트로 등록 되어도 this를 컴포넌트 자신으로 제대로 가리키기 위해, 메서드를 this바인딩(Binding) 하는 작업이 필요하다.

  • 바인딩하지 않은 경우, thisundefined 를 가리키게 된다.

Property Initializer Syntax

앞서 설명한 클래스 컴포넌트 에서의 메서드 바인딩생성자함수 에서 하는 것이 정석이다.

하지만 이 작업을, 새 메서드를 만들 때마다 constructor 도 수정해야하기 때문에 매우 불편하다고 생각할 수 있다.

이 작업을 간단하게 하는 방법이 있고, 그것은 바벨의 transform-class-properties 문법을 사용해서, 결과적으로는 화살표 함수 형태 로 메서드를 정의하면 된다.

import React, { Component } from "react";

export default class EventPractice extends Component {
  state = {
    username: "",
    message: "",
  };

  // 화살표 함수를 통해, 바인딩(binding) 작업이 필요없이, this 는 컴포넌트 자기자신으로 설정된다.
  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="Email 을 입력하세요."
    value={this.state.message}
    onChange={this.handleChange}
    onKeyPress={this.handleKeyPress}
  />
  <button onClick={this.handleClick}>확인</button>
</div>
    );
  }
}
  • 클래스 컴포넌트 에서 화살표 함수로 함수를 정의할 시, this 접근이 바인딩 없이 정상적으로 작성하는 이유는 일반 함수자신이 종속된 객체를 this로 가리키고, 화살표 함수자신이 종속된 인스턴스를 가리키기 때문이다.

객체 안에서 key 를 [ ] 로 감싸기

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

import React, { Component } from "react";

export default 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="Email 을 입력하세요."
    value={this.state.message}
    onChange={this.handleChange}
    onKeyPress={this.handleKeyPress}
  />
  <button onClick={this.handleClick}>확인</button>
</div>
    );
  }
}
  • 동일한 input 태그에 대한 onChange 이벤트를 핸들링 하는 함수지만, 각각의 input 태그에 대한 함수가 아닌 재사용성을 위해 하나의 함수에서 객체 형태의 state 에서 접근 할 때, [ ] 안에 [ 이벤트 속성 ] : 값 형태로 사용한다.
  • 그러면, 동일한 input 태그에 이벤트에 대해, 하나의 메소드에서 각각의 state 업데이트가 가능하다.

이러한 방법은 함수형 컴포넌트에서도 동일하다.

// 함수형 컴포넌트
import React, { useState } from "react";

function EventPractice() {
  // 폼 데이터 인풋이 많을 경우, 하나의 객체 state 에서 관리하는 것이 편함
  // 각 input 에 name 으로 객체의 key 값으로 state 업데이트
  const [form, setForm] = useState({
    username: "",
    message: "",
  });
  const { username, message } = form;
  
  // state 변경시, state 불변성 유지를 위해 spread Syntext 사용
  // 여러 개의 form 데이터가 존재할 경우, 하나의 "객체" 형태에서 관리하고, 이를 업데이트 할 때, "[키] : 값" 형태로 관리 
  const onChangeForm = (e) => {
    const newForm = {
      ...form,
      [e.target.name]: e.target.value,
    };
    setForm(newForm);
  };
  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={onChangeForm}
      />
      <input
        type="text"
        name="message"
        placeholder="Email 을 입력하세요."
        value={message}
        onChange={onChangeForm}
        onKeyPress={onKeyPress}
      />
      <button onClick={onClick}>확인</button>
      <button
        onClick={(e) => {
          e.persist();
          console.log(e.type);
          console.log(e.target.value);

          setTimeout(() => {
            console.warn(e.type);
          }, 2000);
        }}
      >
        Event Persist Test
      </button>
    </div>
  );
}

export default EventPractice;

결론

함수형 컴포넌트 에서도, 여러 개의 인풋 상태(State) 를 관리하기 위해 useState 에서 form 객체 를 사용했다.
useReducer커스텀 Hooks 를 사용하면 이 과정을 더욱 간단하게 처리할 수 있다.
이후에 알아보자.


참고

개념

  • 책 [ 리액트를 다루는 기술 ] 을 기반으로 작성하였습니다.
profile
머쓱이를 좋아합니다 😃

0개의 댓글