(준)공식 문서/React

[ React 공식문서 ] 상호작용 추가하기 (1) : 이벤트에 응답하기

Je-chan 2024. 3. 5. 23:25

1. 이벤트 핸들러 추가하기

  • 이벤트 핸들러를 추가하려면 먼저 함수를 정의한 다음 JSX 태그에 props 로 전달해야 한다
export default function Button() {
  return (
    <button>
      I don't do anything
    </button>
  );
}
  • 이 버튼을 클릭하면 Alert 을 띄우는 기능을 추가하려면 다음의 세 가지 단계를 거쳐야 한다
  1. Button 컴포넌트 안에 handleClick (이름은 단순히 함수명으로 원하는 대로 지으면 됨) 이라는 함수를 선언
  2. 해당 함수 내부의 로직 구현
  3. <button> 태그에 onClick porps 로 handleClick 추가
export default function Button() {
  function handleClick() {
    alert('You clicked me!');
  }

  return (
		<>
		    <button onClick={handleClick}>
		      Click me
		    </button>
			
				{/* 혹은 */}
				<button onClick={function handleClick() {
				  alert('You clicked me!');
				}}>

				{/* 혹은 */}
				<button onClick={() => {
				  alert('You clicked me!');
				}}>
		</>
}
  • 이벤트 핸들러는 컴포넌트 안에서 정의되는 것이 일반적이며 관행상 handle 로 시작하는 이름 뒤에 이벤트 이름이 오도록 한다

2. 이벤트 핸들러에서 props 읽기

  • 이벤트 핸들러는 컴포넌트 내부에서 선언되므로 컴포넌트 props 에 접근할 수 있다
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>
  );
}
  • 이렇게 하면 두 버튼이 서로 다른 메시지를 표시할 수 있다.

3. 이벤트 핸들러를 props 로 전달하기

  • 가끔 부모 컴포넌트가 자식의 이벤트 핸들러를 지정하고 싶을 때가 있다.
  • 이렇게 하기 위해서는 컴포넌트가 부모로부터 받는 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>
  );
}

4. 이벤트 핸들러 props 이름 정하기

  • <button> <div> 와 같은 기본 제공 컴포넌트는 onClick 과 같은 브라우저 이벤트 이름만을 지원한다.
  • 이벤트의 수는 너무 많지만 관례상, on으로 시작하고 이벤트 함수 이름을 카멜케이스로 작성한다
    • on + click ⇒ onClick
  • 이 방식은 props 로 넘길 함수의 이름을 만들 때도 관행적으로 사용된다
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>
  );
}
  • 컴포넌트가 여러 상호작용을 지원하는 경우, 각 서비스의 개념에 맞추어 이벤트 핸들러 props 의 이름을 지정할 수 있다
export default function App() {
  return (
    <Toolbar
			{/* 영화를 재생하는 버튼의 onClick handler 이름 */}
      onPlayMovie={() => alert('Playing!')}
			{/* 이미지 업로드 하는 버튼의 onClick handler 이름*/}
      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>
  );
}
  • 이때 주의할 점은 이벤트 핸들러를 적절한 HTML 태그에 사용해야 한다는 것
    • HTML 표준에 근거하여 작성해야 한다.

5. 이벤트 전파 (Evnet propagation)

  • 이벤트 핸들러는 컴포넌트에 있을 수 있는 모든 하위 컴포넌트의 이벤트도 포착한다.
  • 이벤트가 트리 위로 전파되는 것을 “Bubble” 이라고 한다.
export default function Toolbar() {
  return (
    <div className="Toolbar" onClick={() => {
      alert('You clicked on the toolbar!');
    }}>
      <button onClick={() => alert('Playing!')}>
        Play Movie
      </button>
    </div>
  );
}
  • 여기서 button 을 클릭하면 먼저, “Playing!” 이라는 메시지의 Alert 이 뜨고 그 다음에 “You clicked on the toolbar!” 메시지의 Alert 이 뜬다.
  • 만약 버튼을 제외한 Toolbar 영역을 클릭하면 “You clicked on the toolbar!” 의 메시지 Alert 만 뜰 것
  • onScroll 을 제외한 모든 이벤트는 React 에서 버블을 발생시킨다.

6. 전파 중지하기 (Stopping propagation)

  • 이벤트 핸들러는 이벤트 객체를 유일한 인수로 받는다.
  • 이 이벤트 객체 안에는 이벤트 버블링이 발생하지 못하도록 막아주는 stopPropagation 메서드가 존재해서 이를 호출하면 전파를 중지할 수 있다
function Button({ onClick, children }) {
  return (
		{/* 여기서 e 가 Evnet 객체 */}
    <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>
    </div>
  );
}
  • 이제 버튼을 클릭하면 다음의 과정으로 이벤트가 진행된다
  1. React 는 <button> 에 전달된 onClick 이벤트 핸들러를 호출한다
  2. Button 에 정의된 핸들러는 다음의 기능을 수행한다
    • 이벤트가 더이상 버블링 되지 않도록 e.stopPropagation() 을 호출
    • Toolbar 컴포넌트에서 전달된 props 인 onClick 함수를 호출
  3. Toolbar 컴포넌트에 정의된 이 함수는 “Playing!” 메시지의 Alert 표시
  4. 전파가 중지됐으므로 부모 <div> 의 onClick 핸들러는 실행되지 않는다

(과연 이벤트 버블링은 반드시 막아야 할까? - Liebe 개인 생각)

  • window 시스템 은 사용자의 행동 패턴을 분석하기 위해서 클릭 이벤트 전부를 감지한다.
  • stopPropagation 을 해놓으면 그 영역에서 분석 시스템 코드가 작동이 되지 않는다. 이를 흔히 dead zone 이라 부른다.
  • 버블링을 막아야 하는 상황이 생기면 아키텍쳐를 다시 한 번 생각해보고, 진짜 막아야 하는 상황에서만 버블링을 막는 것이 좋다

캡쳐 단계의 이벤트

  • 드물지만 하위 요소에서 전파가 중지된 경우에도 하위 요소의 모든 이벤트를 포착해야 하는 경우가 존재할 수도 있다.
  • 이런 경우, 이벤트 이름 끝에 Capture 를 추가하면 된다
<div onClickCapture={() => { /* 먼저 실행됨 */ }}>
  <button onClick={e => e.stopPropagation()} />
  <button onClick={e => e.stopPropagation()} />
</div>
  • 각 이벤트는 세 단계로 전파도니다
  1. 아래로 이동하면서 모든 onClickCapture 핸들러를 호출한다
  2. 클릭한 요소의 onClick 핸들러를 실행한다
  3. 상위로 이동하면서 모든 onClick 핸들러를 호출한다

(이벤트 캡쳐링 - 개인 필기)

  • 표준 DOM 이벤트에서 정의한 이벤트 흐름에는 3가지 단계가 존재한다
  1. 캡처링 : 이벤트가 하위 요소로 전파되는 단계
  2. 타깃 : 이벤트가 실제 타깃 요소에 전달되는 단계
  3. 버블링 : 이벤트가 상위 요소로 전파되는 단계
  • 캡처링은 웬만해서는 필요하지 않은 작업이긴 하다.

8. 기본 동작 방지

  • 일부 브라우저 이벤트에는 연결된 기본 동작이 존재한다
  • <form> 의 경우, submit 이벤트는 내부 버튼을 클릭할 때 발생하며, 클릭하면 전체 페이지를 다시 로드한다.
  • 이런 기본 동작을 발생시키고 싶지 않다면 e.prevenDefault() 를 호출하면 된다.
export default function Signup() {
  return (
    <form onSubmit={e => {
      e.preventDefault();
      alert('Submitting!');
    }}>
      <input />
      <button>Send</button>
    </form>
  );
}