개발새발개발/React

React 이벤트 핸들링

birdsfoot 2024. 12. 28.

 

이벤트 핸들링


  • 사용자가 웹 브라우저에서 DOM 요소들과 상호 작용하는 것
  • onmouseover, onclick, onchange 등

 

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

1. 이벤트 이름은 카멜 표기법으로 작성한다

  • onclick (X) onClick(O)
  • onkeyup(X) onKeyUp(O)

 

2. 이벤트에는 함수 형태의 값을 전달한다

  • 실행할 자바스크립트 코드를 직접 전달하지 않고, 함수 형태의 객체를 전달한다
  • 화살표 함수를 바로 만들어 전달해도 되고, 렌더링 부분 외부에 미리 만들어서 전달해도 된다

 

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

  • div, button, input, form, span 등의 요소에는 DOM 이벤트를 설정할 수 있다
  • 하지만 직접 만든 컴포넌트에는 이벤트를 자체적으로 설정할 수 없다
  • ex) MyComponent에 onClick 값을 설정해도 그냥 이름이 onClick 인 props를 전달하는 것 뿐
  • 전달받은 props를 컴포넌트 내부의 DOM 이벤트로 설정하는 것은 가능
  • ex) <div onClick={this.props.onClick}>{...}</div>

 

 

이벤트 종류

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

 

 


 

이벤트 핸들링 실습


실습 순서

  1. 컴포넌트 생성 및 불러오기
  2. onChange 이벤트 핸들링하기
  3. 임의 메서드 만들기
  4. input 여러개 다루기
  5. onKeyPress 이벤트 핸들링하기

 

1. 컴포넌트 생성 및 불러오기

1-1. 컴포넌트 생성

import React, { Component } from 'react';

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

export default EventPractice;

 

 

 

1-2. App.js에서 EventPractice 렌더링

//App.js

import EventPractice from './EventPractice';

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

export default App;

 

 

 

2. onChange 이벤트 핸들링하기

2-1. 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;

 

 

결과

 

  • 콘솔에 기록되는 'e' 객체 = SyntheticEvent : 웹 브라우저의 네이티브 이벤트를 감싸는 객체
  •  네이티브 이벤트와 달리 이벤트가 끝나고 나면 이벤트가 초기화 되므로 정보를 참조할 수 없게 된다
  • 즉, 0.5초 뒤에 e 객체를 참조하면 e 객체 내부의 모든 값이 비워진 상태가 된다
  • 그러므로 만약 비동기적으로 이벤트 객체를 참조할 일이 있다면 e.persist() 함수를 호출해야 한다

 

  • input 상자의 변경사항 기록 :  e.target.value
  onChange={(e) => {
    console.log(e.target.value);
  }}

 

 

 

2-2. state에 input값 담기

  • 생성자 메서드인 constructor에서 state 초기값을 설정하고 이벤트 핸들링 함수 내부에서 this.setState 메서드를 호출하여 state를 업데이트 해보기
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,
            });
          }}
        />
      </div>
    );
  }
}

export default EventPractice;
  • 오류 안나면 성공

 

 

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

  • input comment 값을 alert로 띄운 후 comment를 공백으로 초기화
import React, { Component } from 'react';

class EventPractice extends Component {
  state = {
    message: '',
  };
  render() {
    return (
      <div>
        <h1>이벤트 연습</h1>
        <input (...)/>
        <button
          onClick={() => {
            alert(this.state.message);
            this.setState({
              message: '',
            });
          }}
        >
          확인
        </button>
      </div>
    );
  }
}

export default EventPractice;

 


3. 임의 메서드 만들기

3-1. 기본 방식

  • 이벤트에는 함수 형태의 값을 전달하도록 되어있다
  • 이벤트를 처리할 때 동시에 함수를 만들어서 전달해도 되지만 미리 준비해서 전달하면 가독성이 높다
  • this bind
    • 함수가 호출될 떄 this는 호출부에 따라 결정되기 때문에 클래스의 임의 메서드가 특정 HTML 요소의 이벤트로 등록되는 과정에서 메서드와 this의 관계가 끊어진다.
    • this가 컴포넌트 자신으로 올바르게 가리키기 위해서는 메서드와 this를 바인딩 해야한다(안하면 this가 undefined로 됨)
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.handleClick = this.handleClick.bind(this);
  }

 

 

  • 이벤트 핸들링할 때 미리 만든 임의 메서드로 수정
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>9
        <h1>이벤트 연습</h1>
        <input
          type="text"
          name="message"
          placeholder="아무거나 입력해 보세요"
          value={this.state.message}
          onChange={this.handleChange}
        />
        <button onClick={this.handleClick}>확인</button>
      </div>
    );
  }
}

export default EventPractice;

 

 

3-2. Property Initializer Syntax를 사용한 메서드

  • 기본 방식은 새 메서드를 만들 때마다 constructor도 수정해야 해서 번거롭다
  • 바벨의 transform-class-properties 문법을 사용하여 화살표 함수 형태로 메서드를 정의하면 더 간편하다
class EventPractice extends Component {
  state = {
    message: '',
  };

  handleChange = (e) => {
    this.setState({
      message: e.target.value,
    });
  };

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

 

 


4. input 여러 개 다루기

  • input이 여러개일 경우 event 객체를 활용할 수 있다
  • 객체 안에서 key를 [ ]로 감싸면 그 안에 넣은 레퍼런스의 실제 값이 key로 사용된다
const name = 'variantKey'
const object = {
	[name] : 'value'
}

// 결과 : 'variantKey' : 'value'

 

 

  • handlerChange의 역할
    • input에는 둘 다 name 속성이 있다
    • e.target.name을 사용하여 어떤 입력 필드가 변경되었는지 동적으로 알아내고, 그에 맞는 상태값(this.state.username 또는 this.state.message)를 업데이트 한다
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: '',
    });
  };

 

 

전체 코드

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

export default EventPractice;

 

 

결과

 

 


 

5. onKeyPress 이벤트 핸들링

  • Enter 키를 눌렀을 때 handleClick 메서드가 실행
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>이벤트 연습</h1>
        <input
          type="text"
          name="username"
          placeholder="사용자명"
          value={this.state.username}
          onChange={this.handleChange} 
        />
        <br />
        <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;

 

결과

 

 

onKeyPress 변경사항

  • 표준화된 이벤트가 아니기 때문에 브라우저마다 동작이 다를 수 있어 사용 중단 예정이다
  • onKeyDown(키를 누를 때), onKeyUp(키를 뗄 때) 로 대체되었다

 

 

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


  • e.target.name을 사용하지 않고 onChange 관련 함수 두 개를 따로 만들어준 버전
  • input이 적을 땐 위와같이 해도 괜찮지만, input이 많아지면 e.target.name을 활용하는 것이 더 편하다
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>이벤트 연습</h1>
      <input
        type="text"
        name="username"
        placeholder="사용자명"
        value={username}
        onChange={onChangeUsername}
      />
      <br />
      <input
        type="text"
        name="message"
        placeholder="아무거나 입력해 보세요"
        value={message}
        onChange={onChangeMessage}
        onKeyPress={onKeyPress} // 추가
      />
      <button onClick={onClick}>확인</button>
    </div>
  );
};

export default EventPractice;

 

결과

 

 

  • useState에 문자열이 아닌 객체를 활용하는 방법
  • e.target.name 값을 활용하려면, 아래와 같이 useState를 쓸 때 인풋 값들이 들어 있는 form 객체를 사용하면 됨
import React, { 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}
      />
      <br />
      <input
        type="text"
        name="message"
        placeholder="아무거나 입력해 보세요"
        value={message}
        onChange={onChange}
        onKeyPress={onKeyPress} 
      />
      <button onClick={onClick}>확인</button>
    </div>
  );
};

export default EventPractice;

 

결과 같음

 

 

댓글