Learning React(12. React 불필요한 렌더링 방지)

min seung moon·2021년 8월 16일
0

Learning React

목록 보기
12/15

1. 불필요한 렌더링 방지

01. render 메서드의 정체

  • render의 역할은 각 컴포넌트를 보여주는 일과 부모 컴포넌트로 리턴하기 위한 JSX 생성을 돕는 일을 한다
  • render가 자동으로 호출되는 경우
      1. 컴포넌트의 속성이 갱신된 경우
      1. 컴포넌트의 상태 속성이 갱신된 경우
      1. 부모 컴포넌트의 render 메서드가 호출된 경우
  • 위와 같은 케이스의 경우 굳이 render 메서드가 호출되지 않아도 되는 경우에도 호출이 된다
  • 컴포넌트가 한 두개의 작은 규모이면 크게 성능면으로 차이를 모르지만 앞으로 컴포넌트가 복잡해 질 수록 성능면으로서 크게 문제가 될 수있따

02. render 호출의 최적화

  • 이전 수업의 Slide Menu로 시작할 것이다
  • 이 케이스에서는 MenuButton에 의해 렌더링된 버튼이 클릭되면 MenuContainer 안의 visible이라고 하는 상태 속성의 값이 갱신되고 그 속성으로 인해 class 값이 갱신된다

03. render 호출의 이해

  • 각 컴포넌트의 render 메서드에 console로 호출 순서를 확인해보자!

-1. MenuContainer.js

import React, { Component } from "react";
import MenuButton from "./MenuButton";
import Menu from "./Menu";

class MenuContainer extends Component {
  constructor(props) {
    super(props);

    this.state = {
      visible: false,
    };

    this.handleMouseDown = this.handleMouseDown.bind(this);
    this.toggleMenu = this.toggleMenu.bind(this);
  }

  handleMouseDown(e) {
    this.toggleMenu();

    console.log("clicked");
    e.stopPropagation();
  }

  toggleMenu() {
    this.setState({
      visible: !this.state.visible,
    });
  }

  render() {
    console.log("Rendering : MenuContainer");
    return (
      <div>
        <MenuButton handleMouseDown={this.handleMouseDown} />
        <Menu
          handleMouseDown={this.handleMouseDown}
          menuVisibility={this.state.visible}
        />
        <div>
          <p>Can you spot the item that doesn't belong?</p>
          <ul>
            <li>Lorem</li>
            <li>Ipsum</li>
            <li>Dolor</li>
            <li>Sit</li>
            <li>Bumblebees</li>
            <li>Aenean</li>
            <li>Consectetur</li>
          </ul>
        </div>
      </div>
    );
  }
}

export default MenuContainer;

-2. MenuButton.js

import React, { Component } from "react";
import "./MenuButton.css";

class MenuButton extends Component {
  render() {
    console.log("Rendering : MenuButton");
    return (
      <button
        id="roundButton"
        onMouseDown={this.props.handleMouseDown}
      ></button>
    );
  }
}

export default MenuButton;

-3. Menu.js

import React, { Component } from "react";
import "./Menu.css";

class Menu extends Component {
  render() {
    console.log("Rendering : Menu");
    var visibility = "hide";

    if (this.props.menuVisibility) {
      visibility = "show";
    }
    return (
      <div
        id="flyoutMenu"
        onMouseDown={this.props.handleMouseDown}
        className={visibility}
      >
        <h2>
          <a href="/">Home</a>
        </h2>
        <h2>
          <a href="/">About</a>
        </h2>
        <h2>
          <a href="/">Contact</a>
        </h2>
        <h2>
          <a href="/">Search</a>
        </h2>
      </div>
    );
  }
}

export default Menu;

-4. render 호출 check

  • 이렇게 매번 속성이 바뀔 때마다 모든 컴포넌트의 render가 호출이 된다
    • 하지만 실질적으로 상태가 변하는 MenuContainer와 Menu와 다르게 MenuButton은 속성 변화가 없는 함수를 전달하였다
    • 그렇게 보면 MenuButton까지 render가 호출될 필요는 없어 보인다!


04. render 메서드 호출 방지

-1. shouldComponentUpdate 재정의

  • 이 메서드는 리액트 생명주기 메서드 중 하나이다
  • 이 메서드는 render 메서드의 호출 직전에 호출되며, 이 메서드가 false를 리턴하게 하면 render 메서드 호추릉ㄹ 방지 할 수 있다
  • shouldComponentUpdate를 재정의해 속성이나 상태의 변경 여부를 확인하게 하는 해법

_1. shouldComponentUpdate false return

  • 보면 처음에 화면에 보이기 위한 render 이후 상태가 변경 되었을 때는 호출이 안되었음을 확인할 수 있다!
  • 하지만 무조건 false를 return하는 경우 render가 필요한 상황까지 막아버리기 때문에 shouldComponentUpdate 메서드가 가지고 있는 인수를 사용해서 상황에 맞게 render를 방지해보자!
import React, { Component } from "react";
import "./MenuButton.css";

class MenuButton extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    return false;
  }

  render() {
    console.log("Rendering : MenuButton");
    return (
      <button
        id="roundButton"
        onMouseDown={this.props.handleMouseDown}
      ></button>
    );
  }
}

export default MenuButton;



_2. shouldComponentUpdate used parameters

  • nextProps parameter를 활용하여 현재 내려 받은 props와 메서드 호출 할 때의 props를 비교하여 변화가 있다면 return true를 만약 변화가 없다면 return false를 실행시켜 주도록 한다!
    • 이렇게 해주면 handleMouseDown가 갱신될 경우 return true로 render를 조절할 수 있게 된다
import React, { Component } from "react";
import "./MenuButton.css";

class MenuButton extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.handleMouseDown === nextProps.handleMouseDown) {
      return false;
    } else {
      return true;
    }
  }

  render() {
    console.log("Rendering : MenuButton");
    return (
      <button
        id="roundButton"
        onMouseDown={this.props.handleMouseDown}
      ></button>
    );
  }
}

export default MenuButton;



-3. PureCompoent 사용

  • 관련된 속성이나 상태의 변화가 없음에도 컴포넌트가 다시 렌더링 되는 경우는 흔하다
  • shouldComponentUpdate를 재정의해 속성이나 상태의 변경 여부를 확인하게 하는 해법이 있고 이번에는 그 일을 자동으로 처리해주는 컴포넌트를 사용해볼 예정이다!
  • 단순하게 Component를 PureCompoent로 변경만 해주면 된다 아주 easy하다
    • 그럼 모든 Component를 PureCompoent로 사용하면 되지 않을까?
    • 하지만 PureCompoent는 기본적으로 권장하지 않는다
    • 그 이유는 PureCompoent는 얕은 비교(shallow comparison)를 수행한다
      • 즉 속성이나 상태의 변경 사항을 완벽하게 확인하지 않는다는 말이다
    • 많은 경우에 큰 문제가 없으나, 어떤 경우에는 그렇지 않을 수 있다
      • PureCompoent를 사용할 때 꼭 염두해두어야 하는 상황이다
    • 또한 shouldComponentUpdate를 재정의해 필요한 다른 업데이트 로직을 직접 작성해야 할 필요도 있을 수 있다
      • 그러나 PureCompoent와 shouldComponentUpdate는 동시에 사용할 수 없다
    • PureCompoent 사용 시 가장 큰 문제는 성능이다!
      • 컴포넌트마다 속성이나 상태가 변경됐느지 확인하는 작업은 설사 얕은 비교라 할지라도 연산 시간을 필요로 한다
      • 그 작버은 부모 컴포넌트가 렌더링 될 때마다 자식 컴포넌트의 렌더링 여부를 경정하기 휘해 매변 수행된다는 점을 기억해야한다
      • 특히 복ㅈ바한 UI일 수록 그런 일은 더 자주 일어날 것이다
    • 위 문제만 주의한다면 기본적으로 PureCompoent를 사용해도 문제 없다
import React, { PureComponent } from "react";
import "./MenuButton.css";

class MenuButton extends PureComponent {
  // shouldComponentUpdate(nextProps, nextState) {
  //   if (this.props.handleMouseDown === nextProps.handleMouseDown) {
  //     return false;
  //   } else {
  //     return true;
  //   }
  // }

  render() {
    console.log("Rendering : MenuButton");
    return (
      <button
        id="roundButton"
        onMouseDown={this.props.handleMouseDown}
      ></button>
    );
  }
}

export default MenuButton;



profile
아직까지는 코린이!

0개의 댓글

관련 채용 정보