본문 바로가기

나야, 리액트 스터디

[week3]- 이벤트처리

안녕하세요! YB 이윤지 입니다.ㅎㅎ

이번 주차는 이벤트처리와 useState에 대해서 공부해봤는데요! 그 중에서도 이벤트 처리에 대해서 정리해봤습니다 😊

레쓰고~~

 

*모든 내용은 React 공식문서를 기반으로 작성되었습니다.

 

1. 이벤트 핸들러

이벤트 핸들러는 클릭, 마우스 호버, 폼 인풋 포커스 등 사용자의 상호작용에 따라 유발되는 사용자 정의 함수이다.

다음과 같이 함수를 정의하고 3단계를 거칩니다.

1. Button 컴포넌트 내부에 handleClick 함수 선언하기

2. 해당 함수 내부 로직 구현하기 -> 여기서는 이벤트 메세지를 표시하기 위해 alert 사용

3. <button> JSX에 onclick={handleClick} 추가.

 

여기서! 바로 handleClick 이 이벤트 핸들러

 

주로 컴포넌트 내부에서 정의되며, handle로 시작하고 그 뒤에 이벤트명을 붙인 함수명을 가진다.

 

하지만! 여기서 주의해야 할 것이 있습니다.

 

이벤트 핸들러로 전달한 함수들은 "호출" 이 아닌 "전달" 되어야 합니다.

이 둘의 차이점을 명확히 아시겠나요?

저는... 사실 두 개를 옆에 두고도 뭐가 다른거야? 하고 열심히 쳐다봤는데요...

답은 바로 handleClick() 끝의 () 였습니다.

 

첫 번째 예시의 <button onClick= {handleClick}>

는 onClick 이벤트 핸들러에 "전달" 되었습니다. 이후에 React는 이 내용을 기억하고 오직 사용자가 버튼을 클릭했을 때만 함수를 호출하도록 하는거죠.

 

하지만!

두 번째 예시에서의 <button onClick= {handleClick()}>

저 괄호 하나 들어갔을 뿐인데 뭐가 달라? 했지만

저 끝의 () 가 렌더링 과정 중 클릭이 없었음에도 불구하고 즉시! 함수를 실행하도록 만듭니다.

이는 JSX { 와 } 내의 자바 스크립트가 즉시 실행되기 때문이라고 합니다.

왼쪽이 괄호가 없는거, 오른쪽이 괄호가 존재하는 코드입니다.

코드를 수정하고 새로고침 하자마자 클릭도 안했는데 you clicked me! 가 뜹니다. 호호 신기하다

 

그렇다면 인라인 형태는?

인라인으로 코드를 작성할 때도 동일한 함정이 다른 형태로 나타납니다.

오른쪽과 같이 인라인 함수를 전달하면 버튼을 클릭할 때마다 실행되는 것이 아니라

컴포넌트가 렌더링 될 때마다 실행되겠죠?

 

우리 모두 주의하도록 하자구요!

 

이벤트 핸들러 내에서의 Prop

Button 컴포넌트를 사용하는 위치에 따라 다른 기능을 수행하도록 만들고자 할 때가 있을 거예요.

예를 들면 한 버튼은 영화를 재생하고, 다른 버튼은 이미지를 업로드하는 버튼을 만들고자 할 때가 있죠.

function Button({ onClick, children }) {
  return (
    <button onClick={onClick}>
      {children}
    </button>
  );
}

function PlayButton({ movieName }) {
  function handlePlayClick() {
    alert(`Playing ${movieName}!`);
  }

  return (
    <Button onClick={handlePlayClick}>
      Play "{movieName}"
    </Button>
  );
}

function UploadButton() {
  return (
    <Button onClick={() => alert('Uploading!')}>
      Upload Image
    </Button>
  );
}

export default function Toolbar() {
  return (
    <div>
      <PlayButton movieName="Kiki's Delivery Service" />
      <UploadButton />
    </div>
  );
}

여기서 Toolbar 컴포넌트가 PlayButtonUploadButton을 렌더링 합니다.

PlayButton 은 handlePlayClick을 Button 내 onClick prop으로 전달하고

UploadButton () => alert('Uploading!') Button  onClick prop으로 전달하죠.

최종적으로 Button 컴포넌트는 onClick prop 을 받고 <button>onClick={onClick}으로 직접 전달 하는거죠.

 

이벤트 핸들러 Prop 명명하기

<button>과 <div> 같은 빌트인 컴포넌트는 onClick과 같은 브라우저 이벤트 이름 만을 지원합니다.
그러나 사용자 정의 컴포넌트에서는 이벤트 핸들러 prop의 이름을 원하는 대로 명명할 수 있습니다.

 

컴포넌트가 여러 상호작용을 지원한다면 이벤트 핸들러 prop을 애플리케이션에 특화시켜 명명할 수 있겠죠?

function Toolbar({ onPlayMovie, onUploadImage }) {
  return (
    <div>
      <Button onClick={onPlayMovie}>
        Play Movie
      </Button>
      <Button onClick={onUploadImage}>
        Upload Image
      </Button>
    </div>
  );
}

이렇게 onPlayMovie 와 같이 prop 이름을 애플리케이션별 상호작용에 기반하여 명명하면

나중에 이를 어떻게 이용하게 될 지에 대해 좀 더 알기 쉽겠죠?

 

 

2. 이벤트 전파

이벤트 핸들러는 해당 컴포넌트가 가진 어떤 자식 컴포넌트의 이벤트를 수신할 수도 있습니다. 이를 이벤트가 트리를 따라 “bubble” 되거나 “전파된다”고 표현합니다. 이때 이벤트는 발생한 지점에서 시작하여 트리를 따라 위로 전달됩니다.

 

음...사실 이 부분을 예제를 봐도 이해를 못해서 이건 따로 찾아봤습니다.

쉽게? 말해서

React 에서는 이벤트가 발생할 경우에 이벤트가 시작된 요소로부터 상위 방향으로 이벤트가 순서대로 전파됩니다.

즉, React에서 이벤트 핸들러는 모든 하위 요소에서 발생한 이벤트를 수신할 수 있다는 것인데요.

이 코드를 실행시키면
이렇게 버튼 두 개가 나오겠죠?

play 버튼을 클릭하면 순서대로 play 버튼을 클릭했습니다! <div> 요소를 클릭했습니다! 라고 차례대로 뜨는 것을 확인 할 수 있습니다.

 

마찬가지로 stop 버튼을 클릭하면

play 와 똑같이 순서대로 뜨죠?

 

위 코드에서 버튼을 클릭하면 onClick 이벤트가 발생하고, 해당 <button> 요소의 onClick 이벤트 핸들러가 실행됩니다.

그리고 <button> 의 상위 요소인 <div> 에서도 onClick 이벤트 핸들러가 등록되어 있기 때문에

<div> 의 onClick 이벤트 핸들러도 실행되게 되는거죠!

만약 div 자체를 클릭하면 <div> 요소를 클릭했습니다! 라고 <div> 의 onClick 이벤트 핸들러만 실행됩니다.

React 자습서 내에서는 

부여된 JSX 태그 내에서만 실행되는 onScroll 을 제외한 React 내의 모든 이벤트는 전파됩니다.

라고 합니다. ...왜지?

 

3. 전파 멈추기

이벤트 핸들러는 이벤트 오브젝트를 유일한 매개변수로 받습니다.
관습을 따르자면 “event”를 의미하는 e로 호출되는 것이 일반적이고
이러한 이벤트 오브젝트는 전파를 멈출 수 있게 해줍니다.

 

e.stopPropagation을 추가해서 이벤트 전파를 멈추기!

e.stopPropagation 은 이벤트가 더 이상 bubbling 되지 않도록 방지합니다.

이제 버튼을 클릭해도 더 이상 onClick 의 이벤트의 전파가 발생하지 않는 것을 확인할 수 있었습니다 ㅎㅎ

 

4. 기본 동작 방지하기

일부 브라우저 이벤트는 그와 관련된 기본 브라우저 동작을 가지는데, 일례로 <form> 의 제출 이벤트는 그 내부의 버튼을 클릭 시 페이지 전체를 리로드 합니다.

이러한 일이 발생하지 않기 위해 e.preventDefault() 를 이벤트 오브젝트에서 호출할 수 있습니다.

export default function Signup() {
  return (
    <form onSubmit={e => {
      e.preventDefault();
      alert('Submitting!');
    }}>
      <input />
      <button>Send</button>
    </form>
  );
}

 

 

그렇다면

e.stopPropagation() 과 e.preventDefault 는 무엇이 다를까요?

 

  e.stopPropagation() e.preventDefault()
역할 이벤트의 "전파" 를 중지 브라우저의 "기본 동작" 을 방지
사용 시점 이벤트가 특정 요소에서 발생했을 때,
해당 이벤트가 부모 요소나 다른 상위 요소로
전파되지 않도록 하고 싶을 때 사용
기본 브라우저 동작을 막고,
대신 다른 동작을 정의하고자 할 때 사용
효과 버블링 안됨-> 상위 요소의 이벤트 핸들러가 실행 x 해당 이벤트에 대한 기본 브라우저 동작 중지
ex 특정 버튼 클릭했을 때 상위 요소의 핸들러가
실행되지 않도록 하고 싶을 때
링크 클릭했을 때 기본적으로
다른 페이지로 이동하는 것을 방지
(새로고침...같은거)

 

이렇게 3주차의 한 챕터였던 이벤트 핸들러에 대한 내용 정리를 마치겠습니다!

모두 공부하시면서 어떠셨는지 궁금하네요 ㅎㅎ

저는 이벤트 전파에 대한 내용을 좀 더 깊이 다룰 수 있어서 좋았습니다.ㅎㅎ

처음에는 그냥 <div> 클래스만 없애면 되는거 아니야? 생각했는데

단순히 <div> 클래스를 없애는 것만으로는 해결되지 않는다는 것을 깨달았습니다.. 🤣

 

🧐

그렇다면 많은 요소에 이벤트 리스너를 개별로 다는 대신에 상위 요소에 이벤트 리스너 하나를 추가해

이벤트를 처리하는 방식은 어떤 상황에서 가장 효과적일지, 또 성능에 어떤 영향을 미칠지가 궁금해졌습니다.

아니면 개별로 다는게 좋은것인지?

흠...최적화 하기 위한 방법에는 어떤 것이 있을지...

공부하다 보면 참 궁금한 것들이 많아지네요 🤣

이번 주차도 재밌네요 ㅎㅎ

모두 고생하셨습니다!