본문 바로가기
React

React(16) Create 구현 : shouldComponentUpdate

by 새발개발JA 2021. 1. 8.
반응형

shouldComponentUpdate 란?

  • 얘는 state가 변경되거나 부모 컴포넌트로부터 새로운props를 전달받을 때 실행되는 메소드다.

  • React는 이 메소드(shouldComponentUpdate)의 반환 값에 따라 re-render를 할지에 대한 여부를 결정

  • shouldComponentUpdate 메소드는 true를 반환 (But React 개발자는 re-render를 원치않으면, return false로 놓고 할 수 있다.)


요새 고민은...

<App> 부모가 호출되면 → 하위 자식들 <Subject><Main><Control><ReadContent>등.. 렌더함수가 줄줄이 호출된다. 

그리고 그 자식 중 하나인 <Main> 은 create에 입력하면 그 글을 글 목록에 추가시켜주는 역할을 한다.

BUT 불합리하다. create기능 안써도 <Main>같이 딱히 필요없는 자식도 매번 불필요하게 호출되어야 하다니... 

App.js의 렌더함수의 리턴결과 중 <Main>내용

**코드보기(복사용 첨부 / 더보기클릭)

더보기

import React, { Component } from 'react';

import Subject from "./components/Subject.js";

import Main from "./components/Main.js";

import Control from "./components/Control.js";

import CreateContent from "./components/CreateContent.js";

import ReadContent from "./components/ReadContent.js";

import './App.css';

 

class App extends Component {

  constructor(props){

    super(props);

    this.max_content_id = 3;

    this.state = {   

      mode:'read'

      selected_content_id:1

      head:{title:'WEB'sub:'world wide web!'},

      welcome:{title:'Welcome'desc:'Hello, React!!'},

      contents:[

        {id:1title:'HTML'desc:'HTML is for information'},

        {id:2title:'CSS'desc:'CSS is for design'},

        {id:3title:'JavaScript'desc:'JavaScript is for interactive'}      

      ]

    }

  }

 

  render() {

    console.log('App render');

    var _title_desc_article = null;

    if(this.state.mode ===  'welcome'){

      _title = this.state.welcome.title;

      _desc = this.state.welcome.desc;

      _article = <ReadContent title={_title} desc={_desc}></ReadContent>

    } else if(this.state.mode === 'read'){

      var i = 0;

      while(i < this.state.contents.length){

        var data = this.state.contents[i];

        if(data.id === this.state.selected_content_id) {

          _title = data.title;

          _desc = data.desc;

          break;

        }

        i = i + 1;

      }

      _article = <ReadContent title={_title} desc={_desc}></ReadContent>

    } else if(this.state.mode === 'create'){

      _article = <CreateContent onSubmit={function(_title_desc){

        // add content to this.state.contents

        this.max_content_id += 1;

        var _contents = this.state.contents.concat(

          {id:this.max_content_idtitle:_titledesc:_desc}

        );

        this.setState({

          contents:_contents

        });

        console.log(_title_desc);

      }.bind(this)}></CreateContent>

    }

    return(

      <div className="App">

        <Subject 

          title={this.state.head.title} 

          sub={this.state.head.sub}

          onChangePage={function(){

            this.setState({mode:'welcome'});

          }.bind(this)}>

        </Subject>        

        <Main 

          onChangePage={function(id){

            this.setState({

              mode:'read',

              selected_content_id:Number(id)

            });

          }.bind(this)}

          data={this.state.contents}>

        </Main>

        <Control onChangeMode={function(_mode){

          this.setState({

            mode:_mode

          })

        }.bind(this)}></Control>

        {_article}

      </div>

    );

  }

}

export default App;


 

그래서 shouldComponentUpdate로 고민해결!

얘를 써서 렌더직전에 막아서 사용하지 않을 때 렌더함수가 호출이 되지 않게 만들 수 있다.

이 함수는 두개의 매개변수를 가지도록 되어있다.

 

newProps.data(업데이트된 값) = this.props.data(현재 값) 이면 

→ 값의 변경이 없다는 의미, 렌더함수 실행 x (글목록 클릭만 한다면 Main의 렌더가 실행x)

 

newProps.data(업데이트된 값) != this.props.data(현재 값) 이면 

→ 값의 변경이 있다는 의미, 렌더함수 실행 o (create로 글을 입력하면 Main의 렌더가 실행o)

 

Main.js의 shouldComponentUpdate의 내용

**코드보기(복사용 첨부 / 더보기클릭)

더보기

import React, { Component } from 'react';

 

class Main extends Component {

  shouldComponentUpdate(newPropsnewState){

    console.log(

      '====>Main render shouldComponentUpdate',

      newProps.data,

      this.props.data

    );

    if(this.props.data === newProps.data){

      return false;  

    }

    return true;  

  }  

  render() {

      console.log('Main render');

      var lists = [];

      var data = this.props.data;

      var i = 0;

      while(i<data.length){

        lists.push(

          <li key={data[i].id}>

            <a 

              href={"/content/"+data[i].id} 

              data-id={data[i].id}

              onClick={function(e){

                e.preventDefault();

                this.props.onChangePage(e.target.dataset.id);

              }.bind(this)}>

                {data[i].title}

            </a>

          </li>

        );

        i = i+1;

      }    

      return(

        <nav>

          <ul>

             {lists}

          </ul>

        </nav>

      );

    }

}

export default Main;


 

concat()을 함께 써야하는 이유

shouldComponentUpdate의 값을 비교할 때 push()를 사용하면 원본배열을 건드리기때문에 비교자체가 불가하다 (문제가 생긴다.)

concat을 사용한다면 원본배열은 그대로 유지하면서 업데이트된 값이랑 같은지 비교 가능하기 때문에 문제없이 진행된다.

App.js의 create모드일 때 concat으로 데이터 배열을 _article이라는 복사본으로 업데이트하는 내용

 

실행화면

shouldComponentUpdate가 false일 때, 

글목록을 클릭하면 shouldComponentUpdate가 Main의 렌더를 막는다.

shouldComponentUpdate가 true일 때,

기존의 글목록과 != 새로 추가된 글목록이 다르니 Main의 render함수가 실행된다.

**push와 concat의 차이점(더보기클릭) 

더보기

push와 concat의 공통점은 데이터를 배열 안에 추가할 때 사용한다는 점이다. 

push는 데이터 추가된 사항이 배열의 원본에 적용되며 (마치 파일에 그대로 저장st)

concat은 원본은 그대로 남기고 copy된 배열로 데이터가 추가된 배열이 생성된다. (마치 다른이름으로 저장st)

반응형

댓글