리렌더링과 이벤트 핸들러

</>·2021년 10월 16일
3

Get to Know React

목록 보기
2/8
post-thumbnail

목표

  • 리액트의 리렌더링에 대해서 알아본다.
  • 이벤트 핸들러를 다루어 본다.

1. 리렌더링(Rerendering)

1-1. Vanilla javascript

  <body>
    <div id="root"></div>
    <script>
      const rootElement = document.getElementById("root");
      const getRandomNumber = () => {
        const number = Math.floor(Math.random() * (10 - 1) + 1);
        const element = `
          <button>${number}</button>
          <p>1</p>
        `;
        rootElement.innerHTML = element;
      };

      setInterval(getRandomNumber, 1000);
    </script>
  </body>
  • 위 코드는 1초마다 getRandomNumber 함수를 호출하여 버튼의 숫자를 바꾼다.

rerendering

  • 개발자 도구를 살펴보면 숫자가 바뀌는 button 태그 뿐만 아니라 root div 태그와 p 태그까지 리렌더링 되는 것을 확인할 수 있다. 이는 매우 비효율적이다.

javascript_rerendering


1-2. React

  <body>
    <div id="root"></div>
    <script type="text/babel">
      const rootElement = document.getElementById("root");
      const getRandomNumber = () => {
        const number = Math.floor(Math.random() * (10 - 1) + 1);
        const element = (
          <>
            <button>{number}</button>
            <p>1</p>
          </>
        );
        ReactDOM.render(element, rootElement);
      };

      setInterval(getRandomNumber, 1000);
    </script>
  </body>
  • 리액트 코드도 개발자 도구를 살펴보면 다음과 같다.
  • 숫자가 바뀌는 button태그만 리랜더링 되고 root div 태그와 p 태그는 리렌더링 되지 않는 것을 확인할 수 있다.

react_rerendering

  • 바닐라 자바스크립트만 사용했을 때, 엘리먼트는 변경점이 있을 때마다 다른 엘리먼트의 배치에도 영향을 주기 때문에 비효율적이다.
  • 리액트를 사용하면 효율적으로 바뀐 곳만 렌더링하기 때문에 리플로우나 리페인트를 최소화하는 것에 대한 이점이 있다.

리플로우(reflow)와 리페인트(repaint)

  1. 리플로우는 HTML 문서를 구성하는 요소의 위치나 크기가 변경되었을 때 각 요소를 재배치 하는 작업을 말한다.
  2. 리페인트는 HTML 문서의 전체 또는 일부 영역의 스타일이 변경되었을 때 브라우저가 변경된 스타일을 다시 적용하는 작업을 말한다. 화면의 구조가 바뀌지 않은 이상 리페인트만 실행된다.

1-3. 리렌더링(rerendering) 원리

  • 엘리먼트의 타입이 다르다면? 이전 엘리먼트는 버리고 새로 그린다.

  • 엘리먼트의 타입이 같다면? key를 먼저 비교하고 그 후 props를 비교해서 변경사항을 반영한다. 즉, 리액트는 엘리먼트의 두 속성을 확인하여, 동일한 내역은 유지하고 변경된 속성들만 갱신한다.

  • 이러한 비교 알고리즘을 통해 새로운 엘리먼트 트리를 반환하는 것을 재조정(reconciliation)이라고 하는데, 이는 React 공식 문서에서 확인할 수 있다.

<div className="before" title="stuff" />
<div className="after" title="stuff" />
  • 위 코드에서는 엘리먼트의 타입은 같고(같은 div 엘리먼트) className은 다르기 때문에 리액트는 DOM 노드 상에 className만 수정한다.
<div style={{color: 'red', fontWeight: 'bold'}} />
<div style={{color: 'green', fontWeight: 'bold'}} />
  • 위 코드에서는 엘리먼트의 타입은 같고 style 속성의 color만 다르기 때문에 fontWeight은 수정하지 않고 color만 수정한다.

2. 이벤트(Event)

2-1. Vanilla javascript

    <div id="root">
      <button id="alert">button</button>
    </div>
    <script>
      const button = document.getElementById("alert");
      button.addEventListener("click", () => {
        alert("click");
      });
      button.addEventListener("mouseout", () => {
        alert("mouseout");
      });
    </script>
  • 위 코드는 버튼에 마우스를 클릭할 때는 click이라는 alert창을, 버튼에서 마우스가 벗어 나면 mouseout이라는 alert창을 띄운다.
  • 한 엘리먼트 내에 이벤트는 여러 개가 존재할 수 있다.

2-2. React

    <div id="root"></div>
    <script type="text/babel">
      const rootElement = document.getElementById("root");
      const onClick = () => {
        alert("click");
      };
      const onMouseOut = () => {
        alert("mouseOut");
      };
      const element = (
        <button onClick={onClick} onMouseOut={onMouseOut}>
          button
        </button>
      );
      ReactDOM.render(element, rootElement);
    </script>
  • 리액트의 이벤트 핸들러는 카멜 케이스(camelCase) 로 표기한다.

카멜 케이스(camelCase)

  • 각 단어의 첫문자를 대문자로 표기하고 붙여쓰되, 맨처음 문자는 소문자로 표기하는 방법으 로 띄어쓰기 대신 대문자로 단어를 구분한다.
  • 예) backgroundColor, className 등

2-3. React 이벤트 응용

  <body>
    <div id="root"></div>
    <script type="text/babel">
      const rootElement = document.getElementById("root");

      const state = { keyword: "", typing: false, result: "" };

      const App = () => {
        const onChange = (e) => {
          setState({ keyword: e.target.value, typing: true });
        };

        const onClick = () => {
          setState({
            typing: false,
            result: `find  result of ${state.keyword}`
          });
        };

        return (
          <>
            <input onChange={onChange} />
            <button onClick={onClick}>search</button>
            <p>
              {state.typing ? `searching...  ${state.keyword}` : state.result}
            </p>
          </>
        );
      };

      const setState = (newState) => {
        Object.assign(state, newState);
        render();
      };

      const render = () => {
        ReactDOM.render(<App />, rootElement);
      };

      render();
    </script>
  </body>
  • 위 코드는 input 엘리먼트에 값을 넣어 button 엘리먼트를 누르면 상태를 변화시켜 p 엘리먼트의 내용을 바꾸는 코드이다.
  • App 엘리먼트에의 onChange 함수와 onClick 함수가 그 역할을 한다.
  1. onChange 함수는 input 엘리먼트에 값을 입력하면 setState 함수를 호출한다.

    • setState 함수는 Object.assign을 통해 바뀐 값을 확인해 변경한다.
    • 그리고 setState 함수가 호출 될 때마다 render 함수가 호출된다.
    • render 함수는 input 엘리먼트에 값을 입력해도 p 엘리먼트의 상태가 변하지 않기 때문에 리렌더링 되지 않는다. 따라서, 의도적으로 리렌더링 되도록 하는 역할을 한다.
  2. onClick 함수는 state 객체의 typing 속성을 확인하여 조건에 맞는 값을 찍어낸다.

Object.assign()

  • 출처 객체로부터 모든 열거할 수 있는(enumerable) 하나 이상의 속성(own properties)들을 목표 객체로 복사한다. 이는 수정된 목표 객체를 반환한다.
  • 아래 코드에서 returnedTarget 객체의 c 속성을 7로 수정했을 때 target 객체의 c 속성이 똑같이 변한 것으로 보아 두 객체는 서로 참조되고 있는 것을 확인할 수 있다.
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };

const returnedTarget = Object.assign(target, source);

console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }

console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }

Object.assign(returnedTarget, {c: 7});
console.log(target);
// expected output: Object { a: 1, b: 4, c: 7 }

참고

profile
개발자가 되고 싶은 개발자

0개의 댓글