React 라이브러리

7. 랜더링 최적화: shoulComponentUpdate, PureComponent,React.memo

문정훈 2021. 11. 13. 02:22

1. shouldComponentUpdate

1) 언제 렌더가 다시 일어나냐?

● 해당 컴포넌트의 setState를 했을 때 랜덜링이 다시 일어난다.

컴포넌트의 props의 값이 변경되었을 때 다시 랜더링일 일어난다.

아래 코드를 보면

class Test extends Component {
  state = {
    continue : 0,
  }
  
  onClick = () => {
    this.setState({});
  }
  
  render() {
    conosle.log("랜더링!");
    return (
      <div>
        <button onClick = {this.onClick}>클릭</button>
      </div>
    );
  }
}

module.exports = Test

버튼을 클릭할 때마다 onClick메소드가 호출되는데 setState에서 아무런 state의 값도 변경하지 않았지만 setState를 호출한 것만으로도 랜더링이일어난다. 

이처럼 무의미한 랜더링은 성능 저하로 이어지고 이것을 막을 수 있는 최적화가 필요하다. 

 

 

2) shouldComponentUpdate 메소드

class Test extends Component {
  state = {
    continue : 0,
  }
  
  shouldComponentUpdate(nextProps, nextState, nexstContext) {
    if(this.state.counter !== nextState.counter) {
      return true;
    }
    return false;
  }
  
  onClick = () => {
    this.setState({});
  }
  
  render() {
    conosle.log("랜더링!");
    return (
      <div>
        <button onClick = {this.onClick}>클릭</button>
      </div>
    );
  }
}

module.exports = Test

위 코드와 같이 컴포넌트 내에서 shouldComponentUpdate 메소드를 작성하는데 이 함수가 true를 리턴하면 랜더링이 일어나고 false를 반환하면 랜더링일 일어나지 않는다.

따라서 setState를 호출하면 일단 shouldComponentUpdate 메소드로 1차적 검사가 이루어진다 생각하면 되는데 state의 값이 변경되지 않으므로 메소드는 false를 반환하여 랜더링이 다시 일어나지 않는다. 


 

 

2. pureComponent

shouldComponentUpdate의 사용이 불편하다면 pureComponent를 사용하는 방법도 있다. 아래 코드처럼 Compoennet가 아닌 pureComponent를 사용해보자.

아래처럼 pureComponent를 사용하면 setState가 호출되더라도 state의 값이 변경되었는지를 검사한다. 

pureComponentshouldComponentUudate를 내부적으로 구현한 컴포넌트이다.

import React, { PureComponent } from 'react';

class Test extends Component {
  state = {
    continue : 0,
  }
  
  onClick = () => {
    this.setState({});
  }
  
  render() {
    conosle.log("랜더링!");
    return (
      <div>
        <button onClick = {this.onClick}>클릭</button>
      </div>
    );
  }
}

module.exports = Test

state를 여러개 선언했는데 일반 적인 프로퍼티 타입(배열, 객체 등이 아님)은 값이 바뀌면 shouldComponentUpdate의 값이 true가 되도록 판단을 해주지만 문제는 state 객체의 프로퍼티로 배열, 객체 등 참조 타입을 사용하는 경우이다. 

PureComponent는 참조 타입에서 값이 변경되었는지 판단을 잘 못하는 경향이 있다. 

 

=>결과적으로 참조 타입의 state는 해당 state가 가리키는 참조 타입의 주소가 변경되어야 state가 변경되었다고 감지하고 랜더링을 다시한다. 

 

상황1)

import React, { PureComponent } from 'react';

class Test extends PureComponent {
  state = {
    array:[],
  }
  
  onClick = () => {
    this.setState({
      array : [];
    });
  }
  
  render() {
    conosle.log("랜더링!");
    return (
      <div>
        <button onClick = {this.onClick}>클릭</button>
      </div>
    );
  }
}

module.exports = Test

위 예시는 array에 새로운 배열을 선언한 것이므로 array가 가리키던 주소가 변경된 것이기 때문에 PureComponent는 state 변화를 감지하고 랜더링을 다시한다.

 

 

상황2)

import React, { PureComponent } from 'react';

class Test extends PureComponent {
  state = {
    array:[],
  }
  
  onClick = () => {
    const array = this.state.array;
    array.push(1);
    this.setState({
      array : array;    
    });
  } 
  
  render() {
    conosle.log("랜더링!");
    return (
      <div>
        <button onClick = {this.onClick}>클릭</button>
      </div>
    );
  }
}

module.exports = Test

위 코드를 보면 array는 결과적으로 새로운 요소가 추가되었지만 이것은 PureComponent 입장에서는 array의 상태가 변경된 것이 아니다. 그 이유는 기존의 array가 가지고 있던 배열의 주소를 그대로 사용하기 때문에 즉 참조 값이 바뀌지 않았기 때문에 state 상태가 바뀌지 않은것으로 간주하고 랜더링을 하지 않는다. 

 

 

상황3)

import React, { PureComponent } from 'react';

class Test extends PureComponent {
  state = {
    array:[],
  }
  
  onClick = () => {
    this.setState({
      array: [...this.state.array, 1],
    });
  } 
  
  render() {
    conosle.log("랜더링!");
    return (
      <div>
        <button onClick = {this.onClick}>클릭</button>
      </div>
    );
  }
}

module.exports = Test

위 문법은 배열의 문법인데 []안에 state의 배열을 복사한 새로운 배열을 생성하고 거기다 1을 추가하는 것임 setState안에 array의 참조가 새로운 배열을 참조하므로 랜더링을 다시한다.

 

 

 

3. hooks에서는 어떻게 하나?=>memo

import React, { memo } from 'react';

const Try = memo( ({tryInfo}) => {
  return (
    <div>{tryInfo.try}</div>
    <div>{tryInfo.result}</div>
  )
});

stateprops가 바뀌어야만 즉 hooks에서 pureComponent라고 생가각하면 됨

const Try = memo(여기다가 기존 hooks 선언);

 


 

 

4. 헷갈릴거 정리 : 배열 복사

1) 배열 복사하기

const arr1 = [1,2,3]
const arr2 = [...arr1]
console.log(arr2) // [1,2,3]

 

 

2) 배열 합치기

const arr1 = [1,2,3];
const arr2 = [4,5,6];

const arr3 = [...arr1, ...arr2];
console.log(arr3); // [1,2,3,4,5,6]

'

 

3) 배열 복사 후 요소 추가

const arr1 = [1,2,3];

const arr2 = [...arr1, 4];
console.log(arr2); // [1,2,3,4]