[React] Event Handler 파헤치기

숩딩·2023년 2월 10일
0

React Event Handler

이벤트 처리

리액트의 이벤트 핸들러는 모든 브라우저에서 이벤트를 동일하게 처리하기 위한 SynthticEvent 객체를 전달받는다.

리액트에서 이벤트를 핸들링 할 때 보통 아래처럼 event 를 전달한다.

const onClick = (event) => {
	console.log(event)
}

이 이벤트를 콘솔로 찍어보면 합성 이벤트(SyntheticEvent) 인 것을 알 수 있다.

모든 합성 이벤트 객체는 다음 어트리뷰트를 가진다

boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent
void preventDefault()
boolean isDefaultPrevented()
void stopPropagation()
boolean isPropagationStopped()
void persist()
DOMEventTarget target
number timeStamp
string type

이벤트의 흐름은

  • 캡처링 단계 - 이벤트가 하위 요소로 전파되는 단계
  • 타깃 단계 - 실제 타깃 요소에 전달되는 단계
  • 버블링 단계 - 상위요소로 전파 즉, 가장 내부적인(구체적인) 요소에서부터 바깥 요소까지 이벤트가 퍼지게 되는 것

로 이루어 진다.

아래 이벤트 핸들러는 이벤트 버블링 단계에서 호출된다.

캡처 단계에 핸들러를 등록하기 위해서는 이벤트 이름에 Capture (ex: onClickCapture) 을 사용하면 된다.

자주 쓰이는 이벤트 타입

Form 이벤트

  • onClick (click event)
    : Element 의 마우스나 키보드가 클릭 된 경우
  • onChange (change event)
    : Element 의 내용이 변경 된 경우
  • onSubmit (submit event)
    : Form 의 데이터가 전송될 때

Focus 이벤트

포커스 이벤트는 모든 React DOM 엘리먼트에서 작동한다.

  • onFocus (focus event)
    : Element (혹은 자식 element)가 포커스 된 경우 호출
  • onBlur (blur event)
    : Element(혹은 자식 element)에 포커스가 사라진 경우

**currentTarget / relatedTarget

부모 엘리먼트 바깥 영역으로부터 발생한 이벤트 focus 의 entering 과 leaving 을 감지할 수 있게 currentTarget / relatedTarget을 사용한다. 즉 포커스가 엘리먼트 그 자체에 있는지, 하위 엘리먼트에서 작동하는 지 구별할 수 있는 방법을 보여준다.

keyboard 이벤트

  • onKeyDown (keydown event)
    : 키보드 버튼이 눌린 경우 ( 값 입력 전에 발생, shift, alt, ctrl 등 특수키에만 동작한다. 한/영/한자 등은 인식하지 못함)

  • onKeyUp (keyup event)
    : 키보드 버튼을 눌렀다가 뗀 경우

  • onKeyPress (keypress event)
    : 키보드 버튼이 눌러져 있는 경우 ( 실제 글자가 잘성될 때 동작하는 이벤트 , ASCII 값으로 사용되어 특수키 인식하지 못한다)

Mouse 이벤트

  • onMouseMove (mousemove event)
    : 마우스 커서가 Element 위에서 움직일 때

  • onMouseDown (mousedown event)
    : 마우스 위에서 클릭이 되기 시작할 때

  • onMouseUp (mouseup event)
    :
    마우스 버튼 클릭이 끝날 때

예제

경험상 가장 자주 쓰이는 이벤트는 onClick , onChange 인 것 같다

onChange 기본

input 창에 글씨를 입력하면 change 이벤트가 발생하는데 어떻게 동작하는지 보자

export const Form = () => {
  const [name, setName] = useState("");

  return (
    <div>
          <label htmlFor="name">Name</label>
          <input
            type="text"
            name="name"
            value={name}
          />
    </div>
  );
};

우선 value에 어떤 값이 들어갈지를 지정해줘야 한다. 여기에서는 name input에 변경값을 주고 싶기 때문에 useState로 name state를 만들고 input의 value에 name을 작성해 주었다.

이렇게 해주고 input에 값을 입력하면 input 창의 value 는 계속 빈 값일 것이다.

왜냐? onChange이벤트를 주지 않았으니까 name은 계속 const [name, setName] = useState("");"" 상태에 머물러 있는 것..!

onChange 를 통해 name의 value 값을 업데이트 해주자
위에서 설명했던 이벤트 핸들러가 제공하는 합성 이벤트 객체에 value 값이 담겨있다.
이걸 활용해보자

export const Form = () => {
  const [name, setName] = useState("");

   const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    console.log(e);
  	};
  
  return (
    <div>
          <label htmlFor="name">Name</label>
          <input
            type="text"
            name="name"
            value={name}
            onChange={handleChange}
          />
    </div>
  );
};

input 창에 change 이벤트가 일어나면 콘솔에 event 가 어떻게 나오는지 보자

input 창에 'name' 을 입력하니깐

이렇게 나왔다
내가 예상한 건 name 인데 말이지 ..

왜이런가 하면 지금 onChange에서 setName에 대한 설정이 전혀 없고 콘솔에 only e.target.value 만 나오도록 해서 이렇다.
뭔 말인가 하면 input 에 입력할 때 마다 change 이벤트가 발생하는데 n , a , m , e 이렇게 입력할 때 마다 저 체인지 함수가 동작해서 지렇게 나오는 것!
그렇다면 이제 이 name을 input창에서도 나오게끔 해보자

const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    setName(e.target.value);
	console.log(name);
  };

setName 에 e.target.value 값으로 state 값을 업데이트 해주도록 했고 원하는 대로 input 에 입력한 내용이 잘 보인다!
근데 콘솔에 왜 nam 만 찍히는 걸까? 나는 지금 name을 입력했는데?
그건 바로 setState가 비동기적으로 동작하기 때문에 그렇다..

React는 setState를 킵해뒀다가 다른 기타 코드들을 처리하고 브라우저 이벤트가 끝날 시점에 state를 일괄적으로 업데이트하게 된다.

그럼 동기적으로 사용해야 할 때는? 함수형으로 작성하거나 useEffect로 작성하면 된다

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    setName(e.target.value);
  };

  useEffect(() => {
    console.log(name);
  });

onChange,onClick form 만들어보기

간단하게 name 과 체크박스를 받는 form 을 만들었다.
둘 다 입력이 되면 작성한 name 😇 checked successfully 문구가 나오고
Reset을 클릭하면 모두 초기화가 되어 체크박스의 checked=false , name="" 빈 값이 된다.

사실 어렵지 않게 코드를 생각하고 작성할 수 있다.

import React, { ChangeEvent, useState } from "react";

export const Form = () => {
  const [name, setName] = useState("");
  const [isChecked, setIsChecked] = useState(false);

  const handleClick = () => {
    setIsChecked(!isChecked);
  };

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    setName(e.target.value);
  };

  const handleReset = () => {
    setIsChecked(false);
    setName("");
  };

return (
    <div>
      <h1>Event Handler</h1>
      <br />
      <div>
        <form>
          <label htmlFor="name">Name</label>
          <input type="text" name="name" value={name} onChange={handleChange} />
          <label htmlFor="check1">Check</label>
          <input
            type="checkbox"
            id="check1"
            required
            onClick={handleClick}
            checked={isChecked}
          />
          {name && isChecked && <p>{name}😇 checked successfully!</p>}
          <button onClick={handleReset}>Reset</button>
        </form>
      </div>
    </div>
  );
};

작성 중 의문이 생겼다
이렇게 name 을 받게 하면
input에 입력하는 name 이 그대로~ 화면에 나타난다

근데 이건 어떻게 보면 당연함 .. 지금 name에 handleChange 이벤트가 동작하고 있으니깐 이 name이 입력되는 대로 출력되는 것은 당연하잖아요 ..

애초에 로직을 저렇게 짜면 안되는 것 같다.
submit 버튼을 클릭했을 때만 message가 뜨게끔 수정하고, 둘 중(input,checkbox) 하나의 값이라도 없으면 message는 사라지게 해주었다.

import React, { ChangeEvent, useEffect, useState } from "react";

export const Form = () => {
  const [name, setName] = useState("");
  const [isChecked, setIsChecked] = useState(false);
  const [message, setMessage] = useState("");

  const handleClick = () => {
    setIsChecked(!isChecked);
  };

  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    setName(e.target.value);
  };

  const handleReset = () => {
    setIsChecked(false);
    setName("");
  };

  const handleSubmit = () => {
    setMessage(`${name}😇 checked successfully!`);
  };

  useEffect(() => {
    if (name) {
      setMessage("");
    }
  }, [name]);

  return (
    <div>
      <h1>Event Handler</h1>
      <br />
      <div>
        <div>
          <label htmlFor="name">Name</label>
          <input type="text" name="name" value={name} onChange={handleChange} />
          <label htmlFor="check1">Check</label>
          <input
            type="checkbox"
            id="check1"
            required
            onClick={handleClick}
            checked={isChecked}
          />
          <button onClick={handleReset} type="button">
            Reset
          </button>
          <button onClick={handleSubmit} type="button">
            Submit
          </button>
          {name && isChecked && <p>{message}</p>}
        </div>
      </div>
    </div>
  );
};

submit 을 클릭하고 텍스트를 입력할 때 message 가 그대로 남아있는 현상이.. 마음에 안들었다.
name의 값에 변화가 있을 때 message가 보이지 않아야 하기 때문에 useEffect를 사용하여 name에 변화가 있을 경우 message를 빈 값으로 만들어줬다.

useEffect(() => {
    if (name) {
      setMessage("");
    }
  }, [name]);

이벤트 핸들링 주의 사항

DOM 요소에만 이벤트를 사용할 수 있다.

div, button, input, form, span 같은 DOM 요소에는 이벤트를 설정할 수 있지만, 직접 만든 컴포넌트에는 이벤트를 자체적으로 설정할 수 없다.

<button onClick={handleCLick}>HI</button> // DOM 요소라 onClick 이벤트 설정이 가능
<CustomButton onClick={handleClick}></CustomButton>

위 코드에서 CustomButton 같은 경우 직접 만든 컴포넌트 이다.

CustomButton 에 onClick 값을 설정했지만 이는 함수를 실행하는 것이 아니라 그냥 이름이 onClick 인 props를 CustomButton 에 전달해줄 뿐이다.

profile
Front-End Developer ✨

0개의 댓글