React Advanced

스카치·2023년 2월 23일
0

Optimizing Performance

필요할 때만 렌더 한다.

npx crate-create-app react-advanced
cd react-advanced
code . -r

App.js =>

import React from "react";

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

  componentWillUnmount() {
    console.log("Foo componentWillUnmount");
  }
  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 (
      <span>
        <Foo />
      </span>
    );
  }
}

export default App;

// >>> 같은 컴포넌트의 상위 엘리멘트, props, attribute, 인라인스타일 등이 다르면 
//Foo도 다른 컴포넌트로 인식. 
//=> 1초에 한번씩 Foo는 Didmount, unmount되면서 번갈아 계속 render됨

App.js =>

import React from "react";

class Foo extends React.Component {
  componentDidMount() {
    console.log("Foo componentDidMount", this.props.children);
  }

  componentWillUnmount() {
    console.log("Foo componentWillUnmount");
  }

  static getDerivedStateFromProps(nextProps, prevProps) {
    console.log("Foo getDerivedStateFromProps", nextProps, prevProps);
    return {};
  }
  render() {
    console.log("Foo 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 });
    }, 5000);
  }

  render() {
    if (this.state.count % 2 === 0) {
      return (
        // 같은 key props를 지정해줌으로서 같은 컴포넌트로 인식
        <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;

불필요한 render 멈추기

Class 컴포넌트

shouldComponentUpdate 이용하기

App.js =>

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; // 같은것이니 렌더X
  }
  render() {
    console.log("Person render");
    const { name, age } = this.props;
    return (
      <div>
        {name} / {age}
      </div>
    );
  }
}

class App extends React.Component {
  state = {
    text: "",
    persons: [
      { id: 1, name: "Mark", age: 39 },
      { id: 2, name: "Hanna", age: 28 },
    ],
  };

  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,
      text: e.target.value,
    });
  };
}

export default App;

React.PureComponent 상속받아 이용하기

App.js =>

class Person extends React.PureComponent {
    /*shouldComponentUpdate(previousProps ) {
    	for (const key in this.props){
        	if(previousProps[key] !== this.props[key]) {
            	return true // 새로 렌더
            }
        }
      return false // 같은것이니 렌더X 
    }*/
	render () {
    	console.log('Person render');
    	const {name , age } = this.props
    	return (
      		<div>
              {name} / {age }
      		</div>
        )
    }
  
}

class App extends React.Component {
  state = {
    text: "",
    persons:[
      {id: 1, name: 'Mark', age:39},
      {id: 2, name: 'Hanna', age:28}
    ]
  };

  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
      	text : e.target.value, 
    })
  }
}

export default App;

주의점

App.js =>

import React from "react";

class Person extends React.PureComponent {
  /*shouldComponentUpdate(previousProps ) {
    for (const key in this.props){
        if(previousProps[key] !== this.props[key]) {
            return true // 새로 렌더
          }
      }
    return false // 같은것이니 렌더X 
  }*/
  render() {
    console.log("Person render");
    const { name, age } = this.props;
    return (
      <div>
        {name} / {age}
      </div>
    );
  }
}

class App extends React.Component {
  state = {
    text: "",
    persons: [
      { id: 1, name: "Mark", age: 39 },
      { id: 2, name: "Hanna", age: 28 },
    ],
  };

  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} />
            // return <Person {...person} key={person.id} onClick={() => {}}/>
            // onClick 의 props에 들어간 함수가 매번 새로 만들어지기 때문에 render가 반복된다.
            // 이 경우 따로 메서드를 만들어 넣는게 좋다.
            // ==> toPersonClick = () => {};
            // ==> return <Person {...person} key={person.id} onClick={() => {}}/>
            return (
              <Person
                {...person}
                key={person.id}
                onClick={this.toPersonClick}
              />
            );
          })}
        </ul>
      </div>
    );
  }

  _change = (e) => {
    this.setState({
      ...this,
      text: e.target.value,
    });
  };
  toPersonClick = () => {};
}

export default App;

Function 컴포넌트

App.js =>

import React from "react";

// 1. 함수 컴포넌트를 React.memo로 감싸서 실행
const Person = React.memo(({ name, age }) => {
  console.log("Person render");
  return (
    <div>
      {name} / {age}
    </div>
  );
});

// 2. useCallback의 사용
function App() {
  const [state, setState] = React.useState({
    text: "",
    persons: [
      { id: 1, name: "Mark", age: 39 },
      { id: 2, name: "Hanna", age: 28 },
    ],
  });
  const toPersonClick = React.useCallback(() => {}, []);
  // dependancy 리스트에 의해 처음에만 렌더됨.
  const { text, persons } = state;
  return (
    <div>
      <input type="text" value={text} onChange={change} />
      <ul>
        {persons.map((person) => {
          // return <Person {...person} key={person.id} />
          //return <Person {...person} key={person.id} onClick={() => {}}/>
          // onClick 의 props에 들어간 함수가 매번 새로 만들어지기 때문에 render가 반복된다.
          // 이 경우 따로 메서드를 만들어 넣는게 좋다.
          // ==>
          return <Person {...person} key={person.id} onClick={toPersonClick} />;
        })}
      </ul>
    </div>
  );

  function change(e) {
    setState({
      ...state,
      text: e.target.value,
    });
  }
  // onClick에서 toPersonClick함수가 계속 실행되어 재렌더된다.
  // => useCallback 함수를 이용
  // function toPersonClick() {}
}

export default App;

createPortal

forwardRef

0개의 댓글