강의 유튜브 주소 :

https://www.youtube.com/watch?v=V3QsSrldHqI&list=PLcqDmjxt30RtqbStQqk-eYMK8N-1SYIFn

 

 

React에서 재 렌더링이 발생할때 성능을 생각하면 state나 props가 변경되는 것을 파악해서 재 렌더링이 필요없는 컴포넌트의 재 렌더링을 막아야한다.

 

즉 a 컴포넌트만 달라져야하는 부분이 있을 경우, 기능을 나눌 수 있다면 a와 b로 컴포넌트를 분리해야한다.

성능 최적화를 하기위해서 shouldComponent, pureComponent, useMemo, useCallback을 사용하는것이 좋다.

이것을 알기 위해서는 useEffect를 알아야한다. (이건 더 뒤에 강의에서)

 

class 컴포넌트와 함수형 컴포넌트의 차이.

class 컴포넌트는 render 만 재 렌더링이 되는데 함수형 컴포넌트 같은 경우에는 전체가 다시 재 렌더링이 된다.

함수형 컴포넌트같은 경우 useRef하게 될 경우 

 

ref할 경우 접근

inputEl; 
onRefInput = (c) => { 
	this.inputEl = c; 
}; 

this.inputEl.focus(); 

 

useRef할 경우 접근

import React, {useRef} from 'react';

const Test = () => {
	// 초기 셋팅
	const = inputEl = useRef(null);
	
    const onButtonClick = () => {
    	// useRef는 필수로 current로 접근해야한다.
    	inputEl.current.focus();
    }

  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>버튼</button>
    </>
  );
};

 

jsx에서 if문을 쓰고 싶다면? jsx에서 아래와 같이 쓰면된다.

{
    (() => {
        if(조건){

        }else{

        }
    })()
}

 

반복문을 쓰고 싶다면?

{
    (() => {
        const array = [];
        for(){
            array.push();
        }

        return array;
    })()
}

지저분하다고 느낀다면 자식 컴포넌트로 빼는것이 좋다.

이때 배열이라면 key값이 있어야한다.

return [
  <div key="">사과</div>,
    <div key="">사과</div>,
    <div key="">사과</div>,
];

 

컴포넌트가 쓸데없이 재렌더링이 된다 싶다면 구글 확장프로그램을 이용해서 확인을 해본다.

그 후에 pureComponent를 사용할지 결정하면되는데,

pureComponent는 아래와 같이 선언하는 부분을 변경한 뒤 사용하면된다.(Class 컴포넌트에 해당)

import React, { Component } from 'react';

class Test extends Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

// 아래처럼 변경
import React, { pureComponent } from 'react';

class Test extends pureComponent {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

pureComponent는 props와 state를 얕게 비교하여 변경되었다면 재 렌더링을 해주는 것을 자동으로한다.

만약 복잡한 객체를 비교해야한다면 pureComponent가 아닌 forceUpdate를 사용해야한다고한다(이건 나중에공부하기!)

 

라이프사이클

렌더링이 실행되면 리액트가 돔을 붙여주는 순간 특정한 동작을 할 수 있다.

componentDidMount는 렌더링이 처음 실행되고 성공적으로 실행되었을 때 발생하는 라이플사이클 중에 하나이다.

그 다음 setState로 재렌더링이 되어야할때는 실행되지 않는다.

재렌더링이 실행될 때에는 componentDidUpdate가 일어나며 컴포넌트가 제거될때, 그 직전에는 componentWillMount가 실행된다.

 

만약에 render안에 setState를 사용하면 무한루프가 된다.

render(){
	
    // (X)
    setState({});
    
	return(
    	<>
        	<div>안녕</div>
        </>
    );
};

따라서 render할때 setState의 작업을 하고 싶을때 라이프사이클을 맞게 사용하면된다.

 

class컴포넌트의 경우 constructor가 실행되고 (state나 메서드들)..

처음으로 render가 실행된다. 다음 componentDidMout가 실행되는데 그 전에 ref를 먼저 설정한다.

그리고 setState나 props가 변경될때 재 렌더링이 되면서 componentDidUpdate가 실행된다.

그리고 부모컴포넌트가 자식컴포넌트를 제거할때 자식컴포넌트에서는 componentWillunMount가 실행되고 그 컴포넌트는 소멸된다.

shouldComponentUpdate는 setState나 props가 변경되어 재렌더링이 일어날때 실행되는 시점과 같다.

클래스 -->
constructor -> render -> ref -> componentDidMount -> 
setState/props -> shouldComponentUpdate (true일때)-> 
render-> 
componentDidupdate -> 부모가 나를 삭제 -> componentWillunmount ->소멸

 

강의보면서 글을써서 의식의 흐름대로 쓰느라.. 정신이없는 글이되버리네..

 

강의 유튜브 주소 :

https://www.youtube.com/watch?v=V3QsSrldHqI&list=PLcqDmjxt30RtqbStQqk-eYMK8N-1SYIFn



드디어 4에 들어왔다(한참 남았지만^^;)

 

render 의 return 안에서는 for문과 if를 사용하면 안좋다.(못쓰는 건 아니지만 지저분해지기 때문에 안티패터이될수있다)

 

if문을 보통 삼항연산자로 쓰거나 논리연산자로 사용한다.

jsx에서는 아무것도 없다가 null이다.

render(){
return(
 { this.satate.result.length === 0 ? null : 조건 }
 { this.state.result.lengt !== 0 && 조건 }
);

리액트에서는 jsx의 가독성이 중요하기 때문에 함수로 뺄 수 있다.

renderAverage = () => {
    return this.satate.result.length === 0 ? null : 조건;
};

render(){
    return(
        { this.renderAverage() }
    );
}

 

 

강의 유튜브 주소 :

https://www.youtube.com/watch?v=V3QsSrldHqI&list=PLcqDmjxt30RtqbStQqk-eYMK8N-1SYIFn



리액트에서 돔을 컨트롤할때 ref를 사용하여 접근한다.

Ref는 render 메서드에서 생성된 DOM 노드나 React 엘리먼트에 접근하는 방법을 제공한다.

리액트는 대체로 돔을 컨트롤 하지 않아야하는데, input에 focus하는 작업등을 위해서 사용할 수 있다.

*바람직한 사용 사례는 포커스, 텍스트 선택영역, 미디어 재생을 관리, 애니메이션 직접적 실행, 서드파티 DOM 라이브러리를 React와 사용할때라고한다.

 

jQuery에서는 아래와 같이 작업을 한다.

var input = $('.is-input');

input.focus();

 

React의 class 컴포넌트에서는 아래와 같이 작업한다.

먼저 inputEl을 해당 class 컴포넌트의 멤버로 등록한다.

그리고 난 후 jsx에서 어떤 돔을 컨트롤할지 ref={} 를 통해 선언한다.

이때 함수로 핸들러는 빼는 것이 좋으며 onRefInput에서 보듯이 넘겨받은 인자값을 this.inputEl에 넣어준다.

그 다음 this.inputEl.focus()를 통해 jQuery와 같이 동작하도록 한다.

inputEl;

onRefInput = (c) => {
    this.inputEl = c;
};

this.inputEl.focus();

render(){
    <>
        <form>
            <input ref={this.onRefInput} />
        </form>
    </>
}

 

그렇다면 Hooks에서는 어떻게 사용할 수 있을까?

Hooks에서는 useRef를 사용해서 구현할 수 있다.

const React = require('react');
const {useRef} = React;

const [input, setInput] = useRef(null);

input.current.focus();

return{
    <>
        <form>
            <input ref={input} />
        </form>
    </>
}

다만 useRef를 사용할때에 focus를 한다면 반드시 input.current.focus()에서 current를 써줘야한다.

class에서 ref를 Hooks와 비슷하게 만들어 사용할수도 있다.

(예전에 이런식으로 쓰였다는데 없어진 방법은 아니며 조금이지만 사용되고 있기는 하다고 한다.)

 

요즘은 아래와 같이 createRef()를 통해 간단하게 구현할 수 있다. 

import React, {Component, createRef} from 'react';

inputRef = createRef();

input.current.focus();

render(){
    <>
        <form>
            <input ref={this.inpurRef} />
        </form>
    </>
}

 

Hooks로 useState를 사용하는 것과 다르게 Class에서 setState할때에는 this.setState내부에 다른 동작을 넣을 수 있다.

이런걸 일급함수라고 한다.(hoc)

this.setState( (prevState) => {
    // !!이곳에 다른 동작을 추가할 수 있다.

    return {
        // ...
    }
});

 

만약 render 내부에 setState를 사용한다면 무한루프에 빠지니 주의해야한다.

render는 state가 변경될때 되기 때문에, render -> setState -> render.. 가 반복되니말이다..

render(){

	// 무한루프
    this.setState({});
    
    return{
    }
};

 

자식 컴포넌트에서 props는 절대로 직접적으로 수정하면 안된다.

// 부모
<Welcome name={this.state.name} />

// 자식
class Welcome extends React.Component {
	
  componentDidMount(){
  	// (X)
  	this.props.name = '안녕';  
  }

  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

 

실무에서 props를 변경해야하는 경우가 있는데, 이럴경우에는 부모 컴포넌트의 state를 변경 시켜줘야한다.

 

정말 어쩔수 없이 props를 변경해야할 경우 자식컴포넌트의 state로 선언하여 수정해준다.

근데 이것도 좋은 방법은 아니라고한다.

나중에 알려줄 getDerivedStateFromProps를 사용하면 된다고 한다. (6까지 봤는데, 아직까지 강의에 등장안했다!)

// 부모
<Welcome name={this.state.name} />

// 자식
class Welcome extends React.Component {
  state = {
  	newName : this.props.name
  }
  componentDidMount(){
  
  	this.setState({
    	newName : '안녕'
    });  
  }

  render() {
    return <h1>Hello, {this.state.newName}</h1>;
  }
}

 

 

class 함수 내부에 construct가 사용되는 경우가 있다.

이때 construct 함수를 쓰게되면 미세한 컨트롤이 가능하다고한다.(정밀한 동작, 기본객체로는 힘든것)

constructor(props){
    super(props);
    
    // 이곳에 다른 동작을 추가할 수 있다.
    // 예를 들어서 필터링한 것이라던가?
    const filter = () => {
        return '';
    };
    
    this.state = {
        value : filter()
     };
}

 

강의 유튜브 주소 :

https://www.youtube.com/watch?v=V3QsSrldHqI&list=PLcqDmjxt30RtqbStQqk-eYMK8N-1SYIFn

 

 

리액트 최적화를 알기 위해서는 렌더링되는 부분을 이해해야한다.

class함수는 render부분이 리렌더링되고 함수컴포넌트는 전체가 리렌더링이 된다.

 

import React, { Component } from 'react';

class Test extends Component{
    state = {
        counter : 0
    };

    onClick = () => { 
        this.setState({});
    };

    render(){
        return (
            <div>
                <button onClick={this.onCLick}>클릭</button>
            </div>
        );
    }
}

 

Test코드에서 버튼을 클릭하면 리렌더링이 될까??

onClick 함수에서 setState하고 있지만 state를 변경한게 없다면 리렌더링이 될까?

위 예제에서 버튼을 클릭했을때 변하는 state는 없어도 class는 리렌더링이 된다.

 

리액트는 Props와 State를 변경하게 되면 보통 리렌더링이 일어난다.

바꾸는 State가 없어도 SetState 함수를 호출하게되면 리렌더링이 일어나기 때문이다.

 

이때 class는 두가지의 방법으로 리렌더링을 막고 최적화를 할 수 있다.

 

React의 라이프사이클 ShouldComponentUpdate

 

예제

import React, { Component } from 'react';

class Test extends Component{
    state = {
        counter : 0
    };
	
    // 이부분!
    shouldComponentUpdate(nextProps, nextState){
        if(this.state.counter !== nextStaste.count){
            return true;
        }

        return false;
    }


    onClick = () => { 
        this.setState({});
    };

    render(){
        return (
            <div>
                <button onClick={this.onCLick}>클릭</button>
            </div>
        );
    }
}

 

리액트의 라이프 사이클인 shouldComponentUpdate를 추가한다.

shouldComponentUpdate 함수는 현재 state와 변경되는 state를 비교하여 리렌더링을 할 것인지를 결정하는데,

위의 코드에서 nextState가 변경되는 state이고 this.state가 현재 state이다.

코드의 반환 값이 true일 경우에 리렌더링이 일어나며 false일 경우에 리렌더링이 일어나지 않는다.

따라서 내부에 비교하는 코드를 추가하여 최적화를 할 수 있다.

 

pureComponent

만약에 ShouldComponentUpdate가 복잡하다고 느낄 때에는 간단히 pureComponent를 사용할 수 있다.

Component 대신에 PureComponent를 import하여 extends 다음에 적어준다.

import React, { PureComponent } from 'react';

class Test extends PureComponent{
// ...

이때 ShouldComponentUpdate를 작성해주지 않아도 리렌더링이 일어나지 않게되는데, 

그 이유는 PureComponent 내부에서 shouldComponentUpdate를 자동으로 구현하기 때문이다.

주의해야하는 점은 Props와 State를 비교하도록 구현이 되어있기는 하나 얕은 비교이기 때문에 복잡한 자료 구조일 경우에 의도치 않는 결과가 나올수도 있다.

(객체를 생성할 때 너무 복잡한 구조로 하는것도 좋지 않다.)

그리고 PureComponent 내부에서도 shouldComponentUpdate를 쓸 수 있다고한다.

 

SetState할 경우에 주의해줘야하는 부분이 있는데, 배열이나 객체같은 참조형타입의 불변성 유지이다.

만약 아래와 같이 배열에 push할 경우에는 기존 배열을 참조하고 있기 때문에 pureComponent가 변화를 알아차리기가 어렵다. 불변성을 유지하여 setState를 하자.

// (X)
setState({
	array : this.state.array.push(a)
});

// (O)
setState({
	array : [...this.state.array, a]
});

 

pureComponent가 자동으로 비교하니 shouldComponentUpdate를 안써도 되지않을까 하는데,

각각 필요할 때 사용해야한다.

복잡한 구조일때 정밀한 작업이거나 특정 state가 변경되어도 리렌더링을 방지하고 싶을 경우 shouldComponentUpdate를 통해 의도적으로 막을 수 있기 때문이다.

 

Class에서는 pureComponent나 shouldComponentUpdate를 통해 최적화를 하며 

 

Hooks에서는 memo(Memoization)를 사용하여 최적화한다.

memo는 고차 컴포넌트(Higher Order Component)이며 함수 컴포넌트다.

부모 컴포넌트에서 전달한 props의 변화에 의한 리렌더링을 방지할 때 사용한다.

useState나 useContext 훅을 사용하면 리 렌더링이 일어난다.

import React, { memo } from 'react';

const Test = memo() => {
// ... 

혹은

function Test() {
  return <div>Test이다.</div>;
}

const Memoized = memo(Test);

memo도 얕은 비교로 동작하기 때문에 복잡한 자료구조일 경우 원치않는 동작이 있을 수 있다.

만약 얕은 비교의 기본 동작이 아닌 다른 동작을 원할 경우 두번째 인자를 별도로 제공하면 된다.

function MyComponent(props) {
  /* props를 사용하여 렌더링 */
}
function areEqual(prevProps, nextProps) {
  /*
    nextProp가 prevProps와 동일한 값을 가지면 true를 반환하고, 
    그렇지 않다면 false를 반환!!
    shouldComponentUpdate와 반대로 동작한다.
  */
}
export default React.memo(MyComponent, areEqual);

 

"

pureComponent나 shouldComponentUpdate 그리고 memo는 모두 성능 최적화를 위해 사용한다. 

그렇기 때문에 단순히 렌더링을 방지하기 위해 사용하거나 성능적인 이점이 없다면 사용하지 않는것이 좋다.

"

 


예제 및 지식 참고
https://ko.reactjs.org/docs/react-api.html

+ Recent posts