React 12. Responding to Events

뚜루미·2024년 3월 5일

React

목록 보기
12/39
post-thumbnail

React를 사용하면 JSX에 이벤트 핸들러를 추가할 수 있습니다. 이벤트 핸들러는 클릭, 마우스 오버, 양식 입력 초점 맞추기 등과 같은 상호 작용에 대한 응답으로 트리거되는 자체 함수입니다.

Adding event handlers

이벤트 핸들러를 추가하려면 먼저 함수를 정의한 다음 이를 적절한 JSX 태그에 prop으로 전달합니다.

export default function Button() {
  return (
    <button>
      I don't do anything
    </button>
  );
}

다음 세 단계에 따라 사용자가 클릭할 때 메시지가 표시되도록 할 수 있습니다.

  1. Button 컴포넌트 내부에서 호출되는 함수 handleClick를 선언합니다.
  2. 해당 함수의 내부에 로직을 구현합니다. (메시지를 보여주는 alert 사용)
  3. <button> JSX 에 onClick={handleClick} 을 추가합니다.
export default function Button() {
  function handleClick() {
    alert('You clicked me!');
  }

  return (
    <button onClick={handleClick}>
      Click me
    </button>
  );
}

handleClick 함수를 정의한 다음 이를 props으로 <button>에 전달했습니다. handleClick 은 이벤트 핸들러입니다.

이벤트 핸들러의 기능 :

  1. 일반적으로 컴포넌트 내부에 정의됩니다.
  2. 이름은 handle로 시작하고 그 뒤에 이벤트 이름이 옵니다.

관례적으로 이벤트 핸들러는 handle 뒤에 이벤트 이름을 붙이는 것이 일반적입니다. onClick={handleClick}, onMouseEnter={handleMouseEnter} 등을 자주 보게 됩니다.

또는 JSX에서 이벤트 핸들러를 인라인으로 정의할 수 있습니다.

<button onClick={function handleClick() {
  alert('You clicked me!');
}}>

// 또는 더 간결하게 화살표 기능을 사용하면 다음과 같습니다.
<button onClick={() => {
  alert('You clicked me!');
}}>

이벤트 핸들러에 전달된 함수는 호출되지 않고 전달되어야 합니다. 예를 들어:

passing a function (correct)calling a function (incorrect)
< button onClick={handleClick} >< button onClick={handleClick()} >

그 차이는 미묘합니다. 첫 번째 예에서는 handleClick함수가 이벤트 핸들러로 전달됩니다 onClick. 이는 React가 이를 기억하고 사용자가 버튼을 클릭할 때만 함수를 호출하도록 지시합니다.

두 번째 에에서는 () 끝 부분이 클릭 없이 렌더링하는 동안 즉시 handleClick() 함수를 실행합니다. 이는 JSX {} 내부에서 JavaScript가 바로 실행되기 때문입니다.

코드를 인라인으로 작성하면 동일한 유의점이 다른 방식으로 나타납니다.

passing a function (correct)calling a function (incorrect)
<button onClick={() => alert('...')}><button onClick={alert('...')}>

이와 같이 인라인 코드를 전달하면 클릭 시 실행되지 않습니다. 컴포넌트가 렌더링 될 때마다 실행됩니다.

<button onClick={alert('You clicked me!')}>

이벤트 핸들러를 인라인으로 정의하려면 다음과 같이 익명 함수로 래핑하세요.

<button onClick={() => alert('You clicked me!')}>

렌더링할 때마다 내부에서 코드를 실행하는 대신 나중에 호출할 함수를 생성합니다.

두 경우 모두 전달하려는 것은 함수입니다.

  • <button onClick={handleClick}>handleClick 함수에 전달합니다.
  • <button onClick={() => alert('...')}>() => alert('...') 함수를 전달합니다.

Reading props in event handlers

이벤트 핸들러는 컴포넌트 내부에 선언되므로 컴포넌트의 prop에 접근할 수 있습니다. 아래 예시는 message prop 을 경고로 보여주는 버튼입니다.

function AlertButton({ message, children }) {
  return (
    <button onClick={() => alert(message)}>
      {children}
    </button>
  );
}

export default function Toolbar() {
  return (
    <div>
      <AlertButton message="Playing!">
        Play Movie
      </AlertButton>
      <AlertButton message="Uploading!">
        Upload Image
      </AlertButton>
    </div>
  );
}

두개의 버튼은 새로 다른 메시지를 보여줍니다.

Passing event handlers as props

가끔 부모 컴포넌트가 자신의 이벤트 핸들러를 지정하기를 원할 수 있습니다. Button 컴포넌트를 사용하는 위치에 따라 다른 기능을 실행하고자 할 수도 있습니다. 예를 들어, 하나는 영화 재생을 다른 하나는 이미지 업로드를 할 수 있습니다

이렇게 하려면 컴포넌트가 부모로부터 받는 prop 을 다음과 같이 이벤트 핸들러로 전달합니다.

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을 렌더링합니다.

  • PlayButtonhandlePlayClickonClick prop으로 Button 내부로 전달합니다.
  • UploadButton 은 내부에 있는 Button에 () => alert('Uploading')onClick prop으로 전달합니다.

최종적으로 Button 컴포넌트는 onClick이라는 prop을 전달 받고 해당 prop을 내장 브라우저에 바로 전달합니다. 이는 React가 클릭시 해당 함수를 호출하도록 알려줍니다.

디자인시스템을 사용하는 경우 버튼과 같은 컴포넌트가 스타일을 포함하지만 동작을 지정하지 않는 것이 일반적입니다. 대신 PlayButtonUploadButton 같은 컴포넌트는 이벤트 핸들러를 전달합니다.

Naming event handler props

<button><div> 같은 내장 컴포넌트는 onClick 과 같은 브라우저 이벤트 이름만 지원합니다. 그러나 자신만의 컴포넌트를 만들 때 원하는 방식으로 이벤트 핸들러 prop 의 이름을 지정할 수 있습니다.

이벤트 핸들러 props는 on으로 시작하고 그 뒤에 대문자가 와야 합니다.

예를 들어, Button 컴포넌트의 onClick prop이 onSmash를 호출할 수 있습니다.

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

export default function App() {
  return (
    <div>
      <Button onSmash={() => alert('Playing!')}>
        Play Movie
      </Button>
      <Button onSmash={() => alert('Uploading!')}>
        Upload Image
      </Button>
    </div>
  );
}

이 예시에서는 <button onClick={onSmash}> 브라우저 <button>onClick이라는 prop을 필요로 한다는 것을 보여줍니다. 하지만 사용자 정의 컴포넌트에서 수신하는 prop 이름은 Button 사용자가 결정한다는 것을 보여줍니다.

컴포넌트가 여러 상호 작용을 지원하는 겨우 앱별 개념에 대하여 이벤트 핸들러 prop의 이름을 지정할 수 있습니다. 예를 들어, 이 Toolbar 컴포넌트는 다음 onPlayMovieonUploadImage 이벤트 핸들러를 받을 수 있습니다.

export default function App() {
  return (
    <Toolbar
      onPlayMovie={() => alert('Playing!')}
      onUploadImage={() => alert('Uploading!')}
    />
  );
}

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

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

App 컴포넌트가 ToolbaronPlayMovieonUploadImage를 수행할 것이라는 것을 알 필요가 없다는 점을 유의하세요. 이것이 Toolbar의 구현 세부 사항입니다. 여기서는 ToolbaronClickButton들에 전달합니다. 하지만, 이는 키보드 단축키가 트리거가 되어 이후에 수행됩니다. onPlayMovie와 같은 앱별 구체적인 상호작용을 수행한 후 prop 이름을 지정하면 나중에 유연하게 변경할 수 있습니다.

이벤트 핸들러에 적절한 HTML 태그를 사용하는지 확인하십시오. 예를 들어 클릭을 처리하려면 < button onClick={handleClick} > 대신 < div onClick={handleClick} >을 사용합니다 . 실제 브라우저를 사용하면 < button > 키보드 탐색과 같은 내장된 브라우저 동작이 가능해집니다. 버튼의 기본 브라우저 스타일이 마음에 들지 않고 링크나 다른 UI 요소처럼 보이도록 만들고 싶다면 CSS를 사용하면 됩니다. 접근 가능한 마크업 작성에 대해 자세히 알아보세요.

Event propagation

이벤트 핸들러는 컴포넌트에 있을 수 있는 모든 하위 항목에서도 이벤트를 포착합니다. 이벤트가 트리 위로 bubble 혹은 propagate 된다고 합니다. 이벤트가 발생한 곳에서 시작하여 트리 위로 점진적으로 올라갑니다.

여기 <div> 에는 두 개의 버튼이 잇습니다. 각 <div> 및 버튼에는 모두 자체 onClick 핸들러가 있습니다.

export default function Toolbar() {
  return (
    <div className="Toolbar" onClick={() => {
      alert('You clicked on the toolbar!');
    }}>
      <button onClick={() => alert('Playing!')}>
        Play Movie
      </button>
      <button onClick={() => alert('Uploading!')}>
        Upload Image
      </button>
    </div>
  );
}

두 버튼 중 하나를 클릭하면 해당 버튼의 onClick이 먼저 실행되고 그 다은 부모 <div>onClick 이 실행됩니다. 결국 두개의 메시지가 나타납니다. 툴바를 클릭한 경우 오직 <div>onClick 만 실행됩니다.

모든 이벤트는 연결된 JSX 태그에서만 작동하는 onScroll을 제외한 React에서 전파됩니다.

Stopping propagation

이벤트 핸들러는 이벤트 객체를 유일한 인수로 받습니다. 관례적으로 e 는 이벤트를 의미합니다. 이 개체를 사용하여 이벤트에 대한 정보를 읽을 수 있습니다.

해당 이벤트 개체를 사용하며 전파를 중지시킬 수 있습니다. 이벤트가 상위 컴포넌트에 도달하는 것을 방지하려면 e.stopPropagationButton 컴포넌트와 같이 호출해야 합니다.

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

export default function Toolbar() {
  return (
    <div className="Toolbar" onClick={() => {
      alert('You clicked on the toolbar!');
    }}>
      <Button onClick={() => alert('Playing!')}>
        Play Movie
      </Button>
      <Button onClick={() => alert('Uploading!')}>
        Upload Image
      </Button>
    </div>
  );
}

버튼을 클릭하면

  1. React는 <button> 에 전달된 onClick 핸들러를 호출합니다.
  2. Button에서 정의된 그 핸들러는 다음 과정을 따릅니다.
    • 이벤트 버블링을 방지하기 위해 e.stopPropagation 을 호출합니다.
    • Toolbar 컴포넌트에서 prop으로 받은 onClick 함수를 호출합니다.
  3. Toolbar 컴포넌트에 정의된 해당 함수는 버튼 자체의 경고를 표시합니다.
  4. 전파가 중지되었으므로 상위 <div> 핸들러 onClick이 실행되지 않습니다.

e.stopPropagation()의 결과로, 이제 버튼을 클릭하면 경고가 두 개(<button>과 부모 툴바<div>)가 아닌 단일 경고(<button>에서의)만 표시됩니다. 버튼을 클릭하는 것은 주변 툴바를 클릭하는 것과 동일하지 않으므로, 이 UI에서는 전파를 중지하는 것이 좋습니다.

Capture phase events

드물게 하위 요소의 모든 이벤트가 전파를 중지하더라도 이를 모두 캡처해야 할 수도 있습니다. 예를 들어 전파 논리에 관계없이 모든 클릭을 분석에 기록할 수 있습니다. 이벤트 이름 끝에 Capture 를 추가하면 이 작업을 수행할 수 있습니다

<div onClickCapture={() => { /* this runs first */ }}>
  <button onClick={e => e.stopPropagation()} />
  <button onClick={e => e.stopPropagation()} />
</div>

각 이벤트는 3단계로 전파됩니다:

  1. 아래쪽으로 이동하여 onClickCapture를 모두 호출합니다.
  2. 클릭한 요소의 onClick 핸들러를 실행합니다.
  3. 위쪽으로 이동하여 onClick 핸들러를 모두 호출합니다.

캡처 이벤트는 라우터 또는 분석과 같은 코드에 유용하지만 앱 코드에서는 사용하지 않을 수 있습니다.

Passing handlers as alternative to propagation

이 클릭 핸들러가 한 줄의 코드를 실행한 다음 부모가 전달한 onClick prop을 호출하는 방법에 주목하세요

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

상위 onClick이벤트 핸들러를 호출하기 전에 이 핸들러에 더 많은 코드를 추가할 수도 있습니다. 이 패턴은 전파에 대한 대안을 제공합니다. 하위 컴포넌트가 이벤트를 처리할 수 있도록 하는 동시에 상위 컴포넌트가 몇 가지 추가 동작을 지정할 수도 있습니다. 전파와 달리 자동이 아닙니다. 하지만 이 패턴의 이점은 일부 이벤트의 결과로 실행되는 전체 코드 체인을 명확하게 따라갈 수 있다는 것입니다.

전파에 의존하고 어떤 핸들러가 실행되고 왜 실행되는지 추적하기 어려운 경우 대신 이 접근 방식을 시도해 보십시오.

Preventing default behavior

일부 브라우저 이벤트에는 이와 관련된 기본 동작이 있습니다. 예를 들어, <form>내부의 버튼을 클릭할 때 발생하는 제출 이벤트는 기본적으로 전체 페이지를 다시 로드합니다.

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

e.preventDefault() 함수를 통해 이런 일이 발생하지 않도록 이벤트 객체를 호출 할 수 있습니다 .

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

e.stopPropagation()혼동 하지 마십시오 e.preventDefault(). 둘 다 유용하지만 관련이 없습니다.

Can event handlers have side effects?

이벤트 핸들러는 부작용이 발생하기 가장 좋은 장소입니다.

렌더링 함수와 달리 이벤트 핸들러는 순수 할 필요가 없으므로 무언가를 변경하기 에 좋은 장소입니다 . 예를 들어 입력에 대한 응답으로 입력 값을 변경하거나 버튼 누름에 대한 응답으로 목록을 변경합니다. 그러나 일부 정보를 변경하려면 먼저 해당 정보를 저장할 방법이 필요합니다. React에서는 컴포넌트의 메모리인 상태를 사용하여 이를 수행합니다.

0개의 댓글