re rendering conditions

15702 단어 ReactReact

https://github.com/Ark-inflearn/inflearn-clone-front/issues/83

sprint 라고 할 수 있을지 모르겠지만
Ark 프로젝트 진행을 하다보니 기술부채를 쌓고 넘어갔던 문제들을 이번에 두 가지만 해결하고자 한다.

  • re-rendering 조건
  • 컴포넌트를 잘 분리하는 방법

위 두 가지는 독립적인 주제가 아닐 수 있다.
그렇지만 리렌더링 조건 부터 알아보겠다.

우선 알아보기 전에 내가 알고 있는 것을 미리 적어보려 한다.
내가 알기론 state(redux store 또는 useState)와 prop이 바뀌면 렌더링이 다시 되는 것으로 대략적으로 알고 있다.

그런데 내가 착각했던 것은 state나 prop에 영향을 직접적으로 받는(그 값을 사용하는) 컴포넌트만 바뀌는 줄 알았는데
이번에 이 글 맨 윗 부분링크인 이슈83에선 state를 직접 사용하지 않는 컴포넌트도 다시 렌더링이 되는 현상을 발견했다.
이번 기회에 확실히 컴포넌트가 렌더링이 다시 되는 조건을 명확히 알고 넘어가면 좋겠다는 생각이 들었다.

re-rendering 조건

class component


1. state 변경이 있을 때
2. 새로운 props이 들어올 때
3. 부모 컴포넌트가 렌더링 될 때
4. shouldComponentUpdate에서 true가 반환될 때

  • prop, state가 바뀌면 shouldComponentUpdate를 실행 시켜서 다시 렌더링 되게 함
  1. forceUpdate가 실행될 때
  • props나 state가 아닌 다른 값이 변경되었을 때 리렌더링을 하고 싶다면 그때 사용할 수 있는 메서드 이다.
  • 함수형 컴포넌트에선 사용할 수 없고 대신 useState를 사용하면 된다.

re-rendering test

  1. state 변경이 있을 때
// app.js
import TestPage from './components/TestPage';

const DivDiv = styled.div``;

function App() {
  const [test, setTest] = useState(0);
  function onClickButton() {
    setTest(prev => (prev += 1));
  }
  return (
    <div>
      <nav>
        <button onClick={onClickButton}>test button</button>
        {test}
      </nav>
      <header>
        <div>header</div>
        <div>header</div>
        <div>header</div>
        {/* <DivDiv>header</DivDiv> */}
      </header>
      <main>
        <TestPage />
      </main>
      <footer>
        <div>footer</div>
        <div>footer</div>
        <div>footer</div>
      </footer>
    </div>
  );
}

// TestPage.js
const TestPage = () => {
  return (
    <div>test</div>
  );
};

export default TestPage;

주석을 풀지 않은 경우(styled-components 사용안함)

주석을 풀고 styled-components 사용한 경우

  • state값이 변하면 해당 컴포넌트의 최상위에 위치한 element가 re-rendering된다.
  • import 된 컴포넌트도 re-rendering 된다.
  • styled-components를 사용한 엘리먼트도 re-rendering된다.
  1. prop이 바뀐 경우
  • 테스트는 했으나 첨부는 하지 않겠다.
  • 바뀐 prop이 적용되는 컴포넌트만 re-rendering 된다.

고찰

  • state가 변화하면 styled-component를 사용한 모든 컴포넌트들이 re-rendering되기 때문에 이것이 성능에 얼마나 영향을 미치는지는 따로 확인할 방법을 생각해봐야할 것이다.

  • 자칫 issue #83 만 생각해서 state가 변하면 모든 엘리먼트가 re-rendering되는 줄 알았는데 그것은 아니었다.

    • styled-component를 사용하거나
    • import 된 컴포넌트 이거나
    • 해당 페이지의 최상위에 위치한 컴포넌트들이 re-rendering 되는 것을 확인 할 수 있었다.
  • import된 컴포넌트들이 re-rendering 되는 이유는 조건3. 부모컴포넌트가 렌더링될 때 자식 컴포넌트도 렌더링 되기 때문이다.

  • styled-components가 re-rendering 되는 이유는 두 가지 중 하나로 추정 된다.

    • 1) 자식 컴포넌트로 인식해서
    • 2) {} != {} 에 의해 Virtual DOM이 css-in-js를 사용한 경우 변화를 확인 할 수 없어서
      • emotion도 이 이유로 re-rendering 된다.

  • 위 예제 코드에서 최상위 div대신 <></> 를 사용한 결과 이다.
    최상위에 위치한 div가 사라지면서 그 아래에 있던 nav, header, main, footer 모두가 최상위에 위치되면서 state가 변함에 따라 nav, header, main, footer 모두 re-rendering되는 것을 확인 할 수 있었다.

사실 re-rendering을 고려해서 컴포넌트를 세세하게 작성하기엔 너무 불편할 것이다.
중요한 것은 이것이 성능에 얼마나 악영향을 미치는지 혹은 생각보다 너무 미미해서 티가 아에 안날정도일 수도 있다.

개인적으론 굳이 눈에 띄게 성능에 문제가 있는 경우가 아니라면 굳이 re-rendering을 꼼꼼하게 고려하지 않아도 될 것 같다.

참고

나중에 읽어볼 글

When does React re-render components?

  • React에서의 렌더링에 대한 가장 중요한 개념과 React가 주어진 구성 요소를 다시 렌더링하기로 결정하는 방법.

Rendering in React

  • The red dots represent updates of the DOM tree.
    Updating the VDOM doesn't necessarily trigger an update of the real DOM.
const App = () => {
  const [message, setMessage] = React.useState('');
  return (
    <>
      <Tile message={message} />
      <Tile />
    </>
  );
};

  • The red dots again represent renders.
    In React, this means calling the render function. In the real DOM, this means re-painting the UI.

  • 빨간색으로 표시된 것은 컴포넌트 함수의 실행을 의미하지만 실제로 render 되는 것은 다를 수 있다.

  • 그러나 render 함수의 실행은 두 가지 단점을 가진다.

    • 1) React는 UI를 업데이트해야 하는지 여부를 확인하기 위해 각 구성 요소에서 diffing 알고리즘을 실행해야 합니다.
    • 2) 이러한 렌더 함수 또는 함수 구성 요소의 모든 코드가 다시 실행됩니다.

How to optimize re-renders

  • 비효율적인 재렌더링의 예는 상위 구성 요소가 하위 구성 요소의 상태를 제어하는 경우입니다.

Controlling when a component should update

  • 구성 요소가 업데이트되어야 하는 시점 제어

  • React.memo

  • 페이지 전체 레이아웃에서 페이지가 바뀔 때마다 children 페이지가 적용되는 방식으로 작성된 곳에 memo를 적용해봐도 소용이 없었다.

const CourseLayout = React.memo(({ children }: IProps) => {
  // render page
});
// memo를 적용해도 달라지는 것은 없었다.
  • Be careful where you place your logic. If you put everything in the root component of your application, all the React.memo functions in the world won't help you to fix your performance problems.
  • 이래서 그렇구나

Structure of your components

  • 재렌더링을 개선하는 더 좋은 방법은 코드를 약간 재구성하는 것입니다.
    • useState는 사용하는 컴포넌트에서 선언해서 사용하는게 좋다고 한다.
      • 우리는 카테고리 버튼이 바뀌면 다른 카테고리버튼에 영향을 줘야 되기 때문에 best structure는 아니었지만 부모컴포넌트의 state를 사용할 수 밖에 없었다. 그럼에도 불구하고 이것을 useRef로 개선한 것은 좋은 시도와 경험이었다.

고찰

  • styled-components 또는 emotion을 사용한 css-in-js방식은 react dev tool로 확인하면 계속 re-render 되는 것 처럼 보인다.
    그러나 실제로 re-render 되는 것은 다를 수 있다.
    하지만 최악의 경우 실제로 re-render 될 가능성도 있다.
  • 이걸 확인할 수 있는 방법이 있을까?
    • 개발자 도구에서 확인
      • you can do so in the Chrome DevTools, under the three-dot menu on the right -> More tools -> Rendering -> Paint flashing.

  • styled-components를 사용하면 react dev tool이 re-render되는 것으로 박스표시를 하지만 실제로 DOM에서 re-render되진 않는 것 같다.

  • 크롬 개발자도구에서 More tools -> Rendering -> Paint flashing. 로 확인한 결과 styled-components는 react dev tool에선 re-rendering되는 것 처럼 보이지만 실제론 re-rendering 되지 않았다.

issue83 회고

  • #87에서 개선된 코드가 반영되지 않은 기존코드도 사실은 react dev tool과 달리 실제 브라우저 DOM에 의해 re-render되는 것은 달랐다.
  • 그래도 개선된 코드가 의미가 없는 것은 아니다. render 함수의 실행은 두 가지 단점을 방지 할 수 있기 때문이다.
  • 그리고 실제로 javascript 코드의 실행양 자체가 줄어들어서 성능향상이 있을 수 있다.
  • 다행인 것은 생각보다 react는 똑똑해서 실제로 변화된 element만 re-render한다는 것이다.

참고

좋은 웹페이지 즐겨찾기