본문 바로가기

이것저것 스터디📚/React - 공식 문서

React 공식문서 - Responding to Events(이벤트에 응답하기)

React를 사용하면 JSX에 click, hover, input, focus 등과 같은 상호작용에 반응하여 발생하는 이벤트 핸들러를 추가할 수 있다.

 

Adding event handlers(이벤트 핸들러 추가하기)

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

 

보통 함수의 이름은 hanlde로 시작하는 이름 뒤에 이벤트 이름이 오도록 한다. 예를들어 handleClick, handleMouseEnter 등이 있다.

 

이벤트 핸들러를 prop으로 전달할 때에는 함수를 호출하는게 아니라 전달되어야 한다.

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

Reading props in event handlers(이벤트 핸들러에서 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>
  );
}

Passing event handlers as props(이벤트 핸들러를 props로 전달하기)

 

부모 컴포넌트가 자식의 이벤트 핸들러를 지정할 때에는 부모로부터 받는 prop을 이벤트 핸들러로 다음과 같이 전달하면 된다.

// onClick을 prop으로 받는다
function Button({ onClick, children }) {
  return (
    <button onClick={onClick}>
      {children}
    </button>
  );
}

// handlePlayClick을 prop으로 전달
function PlayButton({ movieName }) {
  function handlePlayClick() {
    alert(`Playing ${movieName}!`);
  }

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

// () => alert('Uploading!')를 onClick prop으로 전달
function UploadButton() {
  return (
    <Button onClick={() => alert('Uploading!')}>
      Upload Image
    </Button>
  );
}

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

 


Naming event handler props(이벤트 핸들러 props 이름 정하기)

 

관례상 이벤트 핸들러 props은 on으로 시작하고 그 뒤에 대문자가 와야하고, 이벤트 핸들러의 prop 이름은 사용자가 지정할 수 있다.

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


// onSmash로 prop 이름 지정
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
      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 컴포넌트는 onPlayMovie 또는 onUploadImage로 어떤 작업을 수행할지 알 필요가 없으며 그것이 Toolbar의 구현 세부 사항이다.

 

* 이벤트 핸들러에 적합한 HTML 태그를 사용해야 한다. 예를들어 클릭을 처리하려면 <div> 태그 대신 <button> 태그를 사용해야 한다.


Event propagation(이벤트 전파)

 

이벤트 핸들러는 컴포넌트에 있을 수 있는 모든 하위 컴포넌트의 이벤트도 포착한다. 즉 이벤트가 트리 위로 '버블' 또는 '전파'되는 것을 이벤트가 발생한 곳에서 시작하여 트리 위로 올라간다고 한다.

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>
  );
}

위 예제에서 <div>는 2개의 버튼을 포함하고 div와 버튼에는 모두 고유한 onClick 핸들러가 있다. 두 버튼 중 하나만 클릭하면 해당 버튼의 onClick이 먼저 실행되고 그 다음에 부모 <div>의 onClick이 실행된다.

 

* 첨부한 JSX 태그에서만 작동하는 onScroll을 제외한 모든 이벤트는 React에서 전파된다.


Stopping propagation(전파 중지하기)

 

이벤트 핸들러는 이벤트 객체를 유일한 인수로 받고 관례상 "event"를 의미하는 e라고 불린다.

 

이벤트가 상위 컴포넌트에 도달하지 못하도록 하려면 e.stopPropagation()을 호출해야 한다.

// 이벤트 전파를 막기 위해 e.stopPropagation 사용
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>
  );
}

* 캡쳐 단계 이벤트

onClickCapture는 이벤트 캡처(Capture) 방식으로 동작하는 이벤트 핸들러이다.

이벤트 캡처는 이벤트 버블링과 반대로 부모 엘리먼트에서 자식 엘리먼트로 이벤트가 전파된다. 즉, 자식 엘리먼트에서 발생한 이벤트가 부모 엘리먼트로 전파되면서 부모 엘리먼트의 onClickCapture 핸들러가 먼저 실행되고, 그 후 자식 엘리먼트의 onClick 핸들러가 실행된다.

const ParentComponent = () => {
  const handleParentClickCapture = () => {
    console.log('Parent component - Click captured in the capture phase');
  };

  const handleParentClick = () => {
    console.log('Parent component - Click bubbled in the bubbling phase');
  };

  return (
    <div onClickCapture={handleParentClickCapture} onClick={handleParentClick}>
      <ChildComponent />
    </div>
  );
};

const ChildComponent = () => {
  const handleChildClickCapture = () => {
    console.log('Child component - Click captured in the capture phase');
  };

  const handleChildClick = () => {
    console.log('Child component - Click bubbled in the bubbling phase');
  };

  return (
    <div onClickCapture={handleChildClickCapture} onClick={handleChildClick}>
      Click me!
    </div>
  );
};
// 1. Parent component - Click captured in the capture phase
// 2. Child component - Click captured in the capture phase
// 3. Child component - Click bubbled in the bubbling phase
// 4. Parent component - Click bubbled in the bubbling phase

Passing handlers as alternative to propagation(전파의 대안으로 핸들러 전달하기)

function Button({ onClick, children }) {
  return (
    <button onClick={e => {
      e.stopPropagation();
      /* 코드 추가 가능 */
      onClick();
    }}>
      {children}
    </button>
  );

위 예제에서처럼 부모 onClick 이벤트 핸들러를 호출하기 전에 이 핸들러에 코드를 더 추가할 수도 있다. 즉, 이벤트 전파를 멈추면서 부모 컴포넌트가 처리해야할 동작들을 추가로 지정할 수 있다는 의미이다.

// 자식 컴포넌트 Button
function Button({ onClick, children, handleAdditionalBehavior }) {
  return (
    <button onClick={(e) => {
      e.stopPropagation();
      console.log('Child component - Click captured in the capture phase');
      // 부모 컴포넌트의 추가 동작
      handleAdditionalBehavior();
      onClick();
    }}>
      {children}
    </button>
  );
}

// 부모 컴포넌트 ParentComponent
function ParentComponent() {
  const handleButtonClick = () => {
    console.log('Parent component - Click captured in the bubbling phase');
  };

  const handleAdditionalBehavior = () => {
    console.log('Additional behavior inside the Button component');
  };

  return (
    <div>
      <Button onClick={handleButtonClick} handleAdditionalBehavior={handleAdditionalBehavior}>Click me</Button>
    </div>
  );
}

 

Preventing default behavior(기본 동작 방지)

 

<form> 태그의 submit 이벤트(내부의 버튼을 클릭할 때 발생하며 전체 페이지를 다시 로드)와 같이 일부 브라우저 이벤트에는 연결된 기본동작이 있다.

 

이때, e.preventDefault()를 호출하여 기본 동작이 발생하지 않게 할 수 있다. 

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

* 참고 : React 공식문서(https://react-ko.dev/learn)