본문 바로가기
React를 같이 배워보자

React16일차-최적화로 돌리기

by 멈추지않아 2022. 2. 5.

오늘은 최대한 랜더나 디스마운트나 이런것을 줄일수 있는 것에 대해서 알아보겟습니다.

어떻게 하면 마운트가 되고 어떻게하면 마운트가 안되고 이런것을 중점을 봐주시면 좋겠습니다.

오늘은 따라하면 좋겠지만 따라하지 않고 글만 보고 이해 할수 있다면 꼭 따라하지 않아도 되요 

안따라하실거면 굵은 글자만 읽고 지나가시면 됩니다.

우선 create react app 만들고 npm start 해주세요

그리고 src/App.js를 변경 오늘은 App.js만 변경 할것 입니다

import "./App.css";

import React from "react";

 

class Foo extends React.Component {

componentDidMount() {

console.log("foo didmount");

}

componentWillUnmount() {

console.log("foo wiilunmount");

}

render() {

return <p>Foo</p>;

}

}

class App extends React.Component {

state = {

count: 0,

};

componentDidMount() {

setInterval(() => {

this.setState({ count: this.state.count + 1 });

}, 1000);

}

render() {

if (this.state.count % 2 === 0)

return (

<div >

<Foo />

</div>

);

return (

<p>

<Foo />

</p>

);

}

}

 

export default App;

 그러모 npm start가서 보면

이렇게 나올 것입니다.  실질적인 내용은 바뀌지 않았는데  이렇게 자꾸 마운트 됫다가 안됬다가  하죠???

이러면 낭비가 됩니다. 

즉, 상위 태그가 다르다면 내용이 같아도 다른것으로 인식되서 내렸다가 올렸다가를 반복하게 됩니다.

 그리고 요소가 바뀌는 이유는 

if (this.state.count % 2 === 0)

return (

<div >

<Foo />

</div>

);

return (

<p>

<Foo />

</p>

이부분에서 if 조건이 참이면 div가 리턴이되고 끝나서 밑에는 실행안하고 참이 아니면 div는 실행이 안되고 밑에 p가 리턴이 되기 때문에 계속 다릏게 바뀌는것입니다.

setInterval(() => {

this.setState({ count: this.state.count + 1 });

}, 1000);

}

그리고 이것이 1초마다 계속 count값을 1을 더해주기때문에 참이었다가 참이 아니었다가 되게 됩니다.

이제 

 

import "./App.css";

import React from "react";

 

class Foo extends React.Component {

componentDidMount() {

console.log("foo didmount");

}

componentWillUnmount() {

console.log("foo wiilunmount");

}

render() {

return <p>Foo</p>;

}

}

class App extends React.Component {

state = {

count: 0,

};

componentDidMount() {

setInterval(() => {

this.setState({ count: this.state.count + 1 });

}, 1000);

}

render() {

if (this.state.count % 2 === 0)

return (

<div className="before">

<Foo />

</div>

);

return (

<div className="after">

<Foo />

</div>

);

}

}

 

export default App;

 

이렇게 바꿔 보시죠

이렇게 div에 클래스 이름이 바뀐다고 해서 마운트되고 안되고 되지는 않습니다. ㅎㅎ 

 

 

이제

import "./App.css";

import React from "react";

 

class Foo extends React.Component {

componentDidMount() {

console.log("foo didmount");

}

componentWillUnmount() {

console.log("foo wiilunmount");

}

render() {

return <p>Foo</p>;

}

}

class App extends React.Component {

state = {

count: 0,

};

componentDidMount() {

setInterval(() => {

this.setState({ count: this.state.count + 1 });

}, 1000);

}

render() {

if (this.state.count % 2 === 0)

return (

<div style={{ color: "red" }}>

<Foo />

</div>

);

return (

<div style={{ color: "green" }}>

<Foo />

</div>

);

}

}

 

export default App;

이렇게 바꺼보시면

또 글자 색깔은 변하지만 마운트됬다가 안됬다가 반복하지 않습니다.  style이 다르다고 왔다 갔다 하지 않습니다 

 

이제

import "./App.css";

import React from "react";

 

class Foo extends React.Component {

componentDidMount() {

console.log("foo didmount");

}

componentWillUnmount() {

console.log("foo wiilunmount");

}

static getDerivedStateFromProps(next, pre) {

console.log("getPRop", next, pre);

return {};

}

render() {

return <p>Foo</p>;

}

}

class App extends React.Component {

state = {

count: 0,

};

componentDidMount() {

setInterval(() => {

this.setState({ count: this.state.count + 1 });

}, 1000);

}

render() {

if (this.state.count % 2 === 0) return <Foo name="mark" />;

return <Foo name="hanna" />;

}

}

 

export default App;

작성

이렇게 하면 마운트 됬다가 안됬다 안하지만 prop의 내용이 바뀌기 때문에

getDerivedStateFromProps 가 반복적으로 실행이 됩니다.
 
또 바꺼 보겠습니다.

 

import "./App.css";

import React from "react";

 

class Foo extends React.Component {

componentDidMount() {

console.log("foo didmount");

}

componentWillUnmount() {

console.log("foo wiilunmount");

}

static getDerivedStateFromProps(next, pre) {

console.log("getPRop", next, pre);

return {};

}

render() {

console.log("render", this.props.children);

return <p>Foo</p>;

}

}

class App extends React.Component {

state = {

count: 0,

};

componentDidMount() {

setInterval(() => {

this.setState({ count: this.state.count + 1 });

}, 1000);

}

render() {

if (this.state.count % 2 === 0)

return (

<ul>

<Foo>second</Foo>

<Foo>third</Foo>

</ul>

);

return (

<ul>

<Foo>first</Foo>

<Foo>second</Foo>

<Foo>third</Foo>

</ul>

);

}

}

export default App;

작성

이렇게 

 

children이 바뀌니까  getDerivedStateFromProps  render 는 Foo갯수 만큼 되지만

unmount 랑 mount는 third만 되는것을 알수 있습니다.third는 있는데 unmount되는게 이상합니다.

이것을 고쳐줘 보겠습니다.

app.js를 

import "./App.css";

import React from "react";

class Foo extends React.Component {
  componentDidMount() {
    console.log("foo didmount");
  }

  componentWillUnmount() {
    console.log("foo wiilunmount", this.props.children);
  }

  static getDerivedStateFromProps(next, pre) {
    console.log("getPRop", next, pre);

    return {};
  }

  render() {
    console.log("render", this.props.children);

    return <p>Foo</p>;
  }
}

class App extends React.Component {
  state = {
    count: 0,
  };

  componentDidMount() {
    setInterval(() => {
      this.setState({ count: this.state.count + 1 });
    }, 1000);
  }

  render() {
    if (this.state.count % 2 === 0)
      return (
        <ul>
          <Foo key={2}>second</Foo>

          <Foo key={3}>third</Foo>
        </ul>
      );

    return (
      <ul>
        <Foo key={1}>first</Foo>

        <Foo key={2}>second</Foo>

        <Foo key={3}>third</Foo>
      </ul>
    );
  }
}

export default App;
변경

이제 보면 제대로 first가 unmount되는것을 확인할수 있습니다. 이렇게 key를 이용해서 뭐가 같은것인지 안내해줄수 있습니다. 순서대로가 아니라 key를 이용해서 같은것 다른것을 구분할수 있었습니다.

 

또 바꾸겠습니다.

import "./App.css";

import React from "react";

 

class Person extends React.Component {

shouldComponentUpdate(previousProps) {

for (const key in this.props) {

if (previousProps[key] !== this.props[key]) return true;

}

return false;

}

 

render() {

console.log("Person render", this.props.children);

const { name, age } = this.props;

return (

<div>

이름:{name},나이:{age}

</div>

);

}

}

class App extends React.Component {

state = {

text: "",

persons: [

{ id: 1, name: "mark", age: 45 },

{ id: 2, name: "mardfdsfk", age: 435 },

{ id: 3, name: "marerwrk", age: 4325 },

],

};

render() {

const { text, persons } = this.state;

return (

<div>

<input type="text" value={text} onChange={this._change} />

<ul>

{persons.map((person) => {

return <Person {...person} key={person.id} />;

})}

</ul>

</div>

);

}

_change = (e) => {

this.setState({ ...this.state, text: e.target.value });

};

}

 

export default App;

작성하고 

이렇게 입력해도 콘솔에 따히 추가로 찍히는게 없습니다.

shouldComponentUpdate(previousProps) {

for (const key in this.props) {

if (previousProps[key] !== this.props[key]) return true;

}

return false;

}

그런데 위에거가 없다면 어떻게 되는지 보게습니다.

이렇게 변할때마다 render 되서 낭비가 됩니다. 이렇게 shouldComponentUpdate를 이용해서 name과 age가 변했는지 안변했는지 확인하고 변했을때만 render시켜주게 해주서낭비를 막았습니다.

이제 변경 해주겠습니다

import "./App.css";
import React from "react";

class Person extends React.PureComponent {
  render() {
    console.log("Person render", this.props.children);
    const { name, age } = this.props;
    return (
      <div>
        이름:{name},나이:{age}
      </div>
    );
  }
}
class App extends React.Component {
  state = {
    text: "",
    persons: [
      { id: 1, name: "mark", age: 45 },
      { id: 2, name: "mardfdsfk", age: 435 },
      { id: 3, name: "marerwrk", age: 4325 },
    ],
  };
  render() {
    const { text, persons } = this.state;
    return (
      <div>
        <input type="text" value={text} onChange={this._change} />
        <ul>
          {persons.map((person) => {
            return <Person {...person} key={person.id} onClick={this.percli} />;
          })}
        </ul>
      </div>
    );
  }
  _change = (e) => {
    this.setState({ ...this.state, text: e.target.value });
  };
  percli = () => {};
}
export default App;
 
작성

 onClick={this.percli},percli = () => {};

 

여기서는 이 두부분이 중요합니다 

이 코드대로는 입력해도 추가로 render가 되지 않습니다. 하지만

onClick={() => {}}
 이렇게 바꺼준다면 render 발생해버립니다.

이부분을 조심하여 작성하여야 합니다.

 

이제 두개 남았습니다

import "./App.css";

import React from "react";

 

const Person = React.memo(({ name, age }) => {

console.log("Person render");

 

return (

<div>

이름:{name},나이:{age}

</div>

);

});

class App extends React.Component {

state = {

text: "",

persons: [

{ id: 1, name: "mark", age: 45 },

{ id: 2, name: "mardfdsfk", age: 435 },

{ id: 3, name: "marerwrk", age: 4325 },

],

};

render() {

const { text, persons } = this.state;

return (

<div>

<input type="text" value={text} onChange={this._change} />

<ul>

{persons.map((person) => {

return <Person {...person} key={person.id} onClick={this.percli} />;

})}

</ul>

</div>

);

}

_change = (e) => {

this.setState({ ...this.state, text: e.target.value });

};

percli = () => {};

}

 

export default App;

 

이렇게 React.memo를 활용해서 class Person을 함수형식으로 바꺼줄수 있습니다.

 

import "./App.css";

import React from "react";

 

const Person = React.memo(({ name, age }) => {

console.log("Person render");

 

return (

<div>

이름:{name},나이:{age}

</div>

);

});

function App() {

const [state, setState] = React.useState({

text: "",

persons: [

{ id: 1, name: "mark", age: 45 },

{ id: 2, name: "mardfdsfk", age: 435 },

{ id: 3, name: "marerwrk", age: 4325 },

],

});

const percli = React.useCallback(() => {}, []);

return (

<div>

<input type="text" value={state.text} onChange={change} />

<ul>

{state.persons.map((person) => {

return <Person {...person} key={person.id} onClick={percli} />;

})}

</ul>

</div>

);

function change(e) {

setState({ ...state, text: e.target.value });

}

}

 

export default App;

이렇게 바꺼주면 React.useCallback을  사용해서percli이 특정 데이터가 바꼈을떼만 동작하게 만들수 있습니다.