본문 바로가기

나야, 리액트 스터디

[week3] 이벤트 핸들러 및 전파, State

 

안녕하세요! 물결웹팟 OB 박채연입니다 😋 

이번주차엔 이벤트 및 State에 관련된 내용을 공부했는데요,

간단한 상태를 다루는 주차라 공식문서의 모든 내용보단 제가 잘 모르고 넘어갔던 내용들을 깊게 파보고자 해봤습니다!

 

우리가 프로젝트 만들 때 수많은 상태를 만들고 사용하는데,

막상 state에 대해 깊게 찾아보고 공부한 적은 없는 것 같더라구요 -!

그래서 이번주, 다음주 동안 state에 대해 자세히 알아보면서 리액트에 더더욱 딥다이브 해보면 좋을 것 같습니다 💨

 

 


 

 

 

📍 이벤트 핸들러

클릭, 마우스 호버, 폼 인풋 포커스 등 사용자 사용작용에 따라 유발되는 사용자 정의 함수

 

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

함수를 전달하면 사용자가 버튼을 클릭했을 때만 함수를 호출하지만, 함수를 호출하면 렌더링 과정 중 클릭이 없었음에도 불구하고 즉시 함수를 실행하도록 만든다. JSX { 중괄호 } 내의 자바스크립트는 즉시 실행되기 때문!

// 함수 전달하기
<button onClick={handleClick} /> 
<button onClick={() => alert('...')} />

// 함수 호출하기
<button onClick={handleClick() />} />
<button onClick={alert('...')} />

 

 

 

📍 이벤트 전파

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이 실행된다!

 

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

 

이벤트 오브젝트를 이용해서 이벤트가 부모 컴포넌트에 닿지 못하도록 e.stopPropagation()을 호출할 수 있다.

즉, 이벤트가 더이상 버블링 되지 않도록 방지한다.

기본 동작을 방지하기 위해선 e.preventDefault()를 호출하면 된다.

 

정리해보자면,

e.stopPropagation()  이벤트 핸들러가 상위 태그에서 실행되지 않도록 멈춘다.
e.preventDefault()  기본 브라우저 동작을 가진 일부 이벤트가 해당 기본 동작을 실행하지 않도록 방지한다.

 

 

👀 이벤트 핸들러가 사이드 이펙트를 가진다는 것

이벤트 핸들러는 함수 렌더링과 다르게 순수할 필요가 없기 때문에, 무언가를 변경하기 좋다.

일부 정보를 수정하기 위해선 기존의 정보를 저장하기 위한 수단이 필요한데, 이때 사용하는 수단이 바로 state이다.

 

 


 

 

📍 State

렌더링 사이에 데이터를 유지하고, state

리액트가 새로운 데이터로 컴포넌트를 렌더링 하도록 유발하는 stateSetter

 

리액트 훅은 컴포넌트 최상위 수준 또는 커스텀 훅에서만 호출이 가능해서, 조건문, 반복문, 기타 중첩 함수 내부에서 호출할 수 없으며, 파일 상단에 모듈을 import 하는 것과 유사하게 컴포넌트 상단에 리액트 기능을 사용한다고 생각하면 된다.

 

state는 격리되고, 비공개로 유지된다. 컴포넌트 인스턴스에 지역적이란 뜻!

 

 

📍 렌더링

 

1️⃣ 렌더링 트리거

[컴포넌트의 초기 렌더링인 경우]

import Image from './Image.js';
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'))
root.render(<Image />);

앱을 시작할 때, 초기 렌더링 트리거가 필요한데, 대상 DOM 노드와 함께 createRoot를 호출한 다음,

해당 컴포넌트로 render 메서드를 호출하면, 이 작업이 완료된다!

 

[컴포넌트의 state가 업데이트 된 경우]

set 함수를 통해 상태를 업데이트하여, 추가적인 렌더링을 트리거 할 수 있다.

컴포넌트 상태를 업데이트하면, 자동으로 렌더링 대기열에 추가된다.

 

 

2️⃣ 컴포넌트 렌더링 

렌더링 트리거 후, 컴포넌트를 호출해 화면에 표시할 내용 파악

초기 렌더링에서 리액트는 루트 컴포넌트를 호출하며,

이후 렌더링에선 state 업데이트가 일어나 렌더링을 트리거한 컴포넌트를 호출한다.

 

렌더링은 항상 순수한 계산이므로, 동일한 입력엔 동일한 출력을 해야하고 렌더링 이전의 state를 변경해선 안된다.

 

 

3️⃣ DOM에 커밋

초기 렌더링의 경우, appendChild() DOM API를 사용해 생성한 모든 DOM 노드를 화면에 표시한다.

리렌더링의 경우, 렌더링하는 동안 계산된 최소한의 작업을 적용해 DOM이 최신 렌더링 출력과 일치하도록 한다.

 

렌더링이란, 리액트가 컴포넌트, 함수를 호출한다는 뜻 ➡️ 해당 함수에서 반환하는 JSX는 시간 상 UI의 스냅샷과 같음.

prop, 이벤트 핸들러, 로컬 변수는 모두 렌더링 당시 state를 사용해 계산된다.

 

state를 설정하면, 다음 렌더링에 대해서만 변경된다.

state 변수 값은 이벤트 핸들러의 코드가 비동기적이더라도 렌더링 내에서 절대 변경되지 않는다!

컴포넌트를 호출해 리액트의 UI가 스냅샷을 찍을 때 고정된 값이란 뜻이다.

 

 


 

그동안 count 관련 예시를 보면서, 결과에 대해선 알고 있었지만 그 원리에 대해선 깊게 이해하지 못하고 있었거든요!

근데 이게 리액트가 state 관련해서, UI를 스냅샷 찍듯 관리하기 때문에

고정된 값이 나온다는 그런 원리를 이번에 깨닫게 되어서 참 좋네요 🤔