[벨로퍼트님 개념] 1장(3) 리액트

초록귤·2022년 1월 13일
8
post-thumbnail

16 ~ 18장
useEffect | useMemo | useCallback

16. useEffect를 사용하여 마운트/언마운트/업데이트시 할 작업 설정하기

  • 마운트 (처음 나타났을 때)
  • 언마운트 (사라질 때)
  • 업데이트 ( 특정 props가 바뀔 때)

특정 작업을 처리하는 방법

    1. 한 번만 랜더링된다.
    1. color 변경될때마다 렌더링된다. (버튼 클릭 경우가 아닌 색 변경 기준)
    1. 3개 값 중 어느 값 하나라도 바뀌면 렌더링된다.
    1. 이 컴포넌트가 해체되기 바로 직전에 run하는 코드 = return 사용
      컴포넌트 run 할 때, 네트워크 / API / 이벤트리스너 => 사용안할 때 청소해야한다. 연결하고, 끊고. useEffect(() => {return() => {// 해체되기 직전에 네트워크 / API remove connection }}

마운트 / 언마운트 관리

useEffect를 사용방법
useEffect( 함수 , 의존값이 들어있는 배열(deps))
만약 deps 배열을 비우게 된다면, 컴포넌트가 처음 나타날때에만 useEffect에 등록한 함수가 호출된다.

그리고, useEffect에서는 함수를 반환할 수 있는데, 이를 cleanup 함수라고 부른다.

  • cleanup 함수는 useEffect에 대한 뒷정리를 해준다고 이해하면 된다.
  • deps가 비어있는 경우에는 컴포넌트가 사라질 때 cleanup 함수가 호출된다.

😇 주로 마운트 시에 하는 작업(처음 나타났을 때)

  • props로 받은 값을 컴포넌트의 로컬 상태로 설정
  • 외부 API 요청 ( REST API 등)
  • 라이브러리 사용( D3, Video.js 등..)
  • setInterval를 통한 반복작업 혹은 setTimeout을 통한 작업 예약

🙈 주로 언마운트 시에 하는 작업 (사라졌을때)

  • setInterval, setTimeout을 사용하여 등록한 작업들 clear하기
    ( clearInterval, clearTimeout)
  • 라이브러리 인스턴스 제거

deps에 특정 값 넣기

  • 넣게 된다면?
    컴포넌트가 처음 마운트 될 때에도 호출이 되고, 지정한 값이 바뀔 때에도 호출이 된다.
  • 언마운트시에도 호출이 되고, 값이 바뀌기 직전에도 호출이 된다.
  • <규칙> useEffect 안에서 사용하는 상태나, props가 있다면 useEffect의 deps에 넣어주어야 한다.
  • 만약 넣지 않는다면?
    useEffect에 등록한 함수가 실행될 때 최신 props/상태를 가르키지 않게 된다.

deps 파라미터를 생략하기

  • 컴포넌트가 리렌더링 될 때마다 호출이 된다.

  • 참고로 리액트 컴포넌트는 기본적으로 부모 컴포넌트가 리렌더링되면 자식 컴포넌트 또한 리렌더링이 된다. (바뀐 내용이 없다할지라도!!)

  • 물론, 실제 DOM에 변화가 반영되는 것은 바뀐 내용이 있는 컴포넌트에만 해당한다. 하지만 Virtual DOM에는 모든걸 다 렌더링하고 있다는 거다.

  • 나중에는, 컴포넌트를 최적화 하는 과정에서 기존의 내용을 그대로 사용하면서 Virtual DOM에 렌더링하는 리소스를 아낄 수도 있다.

useEffect


마운트 : 처음 나타남
언마운트 : 사라짐

구조


첫번째 인자는 함수, 두번째 인자는 배열(주로 deps 라고 칭한다.)이 들어간다.

cleanup 함수


useEffect 안에서 return 할 때 실행된다.
useEffcet의 뒷정리를 한다. -> state에서 값 지울때 실행됨

deps


deps 에 특정값을 넣게 되면, 컴퍼넌트가 마운트 될 때, 지정한 값이 업데이트 될 때 useEffect 실행
deps에 값이 없다면 useEffect가 최신 값을 가리키지 않게 된다.
deps에 값이 없다면 컴포넌트가 리렌더링 될 때마다 호출이 된다.
deps에 값을 넣는것을 기본이라고 생각하는게 좋다.

17. useMemo 를 사용하여 연산한 값 재사용


출처 -개발화라리 유튜버님

  • 성능 최적화를 위하여 연산된 값을 useMemo 라는 Hook을 사용하여 재사용하는 방법을 알아보자!App 컴포넌트에서 다음과 같이 countActiveUsers라는 함수를 만들어서, active 값이 true인 사용자의 수를 세어서 화면에 렌더링해보기.

추가된 부분

function countActiveUsers(users) {
 console.log('활성 사용자 수를 세는중...');
 return users.filter(user => user.active).length;
}
const count = countActiveUsers(users);
 <div>활성사용자 수 : {count}</div>
 
  • countActiveUsers 함수에서 콘솔에 메시지를 출력하도록 한 이유는, 이 함수가 호출될때마다 우리가 알 수 있게 하기 위함이다.

    • 다른 계정명을 눌러서 초록색으로 만들면 활성 사용자 수 또한 업데이트 됨
    • 그런데 여기서 발생하는 성능적 문제
      ! 바로 input 값을 바꿀때에도 countActiveUsers 함수가 호출된다는 것 !

    => 활성 사용자 수를 세는건, users 에 변화가 있을때만 세야되는건데, input 값이 바뀔 때에도 컴포넌트가 리렌더링 되므로 이렇게 불필요할때에도 호출해서 자원이 낭비된다

    이러한 상황에서 useMemo라는 Hook함수를 사용하면 성능을 최적화할 수 있다.

    Memo = 'Memoized" 의미 = '이전에 계산한 값을 재사용한다' 는 의미

    [사용방법]

    useMemo( 어떻게 연산할지 정의하는 함수 , deps배열)

    이 배열 안에 넣은 내용이 바뀌면, 우리가 등록한 함수를 호출해서 값을 연산해주고, 만약에 내용이 바뀌지 않았다면 이전에 연산한 값을 재사용하게 된다.

추가된 부분

import React, { useRef, useState, useMemo } from 'react';
const count = useMemo(() => countActiveUsers(users), [users]);

18.useCallback을 사용하여 함수 재사용

  • useCallback은 useMemo와 비슷한 Hook이다.

    • useMemo는 특정 결과값을 재사용할 때 사용
    • useCallback은 특정함수를 새로 만들지 않고 재사용하고 싶을때 사용한다.
      (의존성 없앤 [ ]사용한 경우만)
  • 이벤트 핸들러 함수나 api를 요청하는 함수를 주로 useCallback으로 선언한다.

  • 이전에 App.js에서 구현했었던 onCreate, onRemove, onToggle 함수를 확인하면

    • 이 함수들은 컴포넌트가 리렌더링 될 때마다 새로 만들어진다.
    • 함수를 선언하는 것 자체는 사실 메모리/CPU 도 리소스를 많이 차지하는 작업 아니다. = 그 자체만으로 큰 부하가 생길 일 없다
  • 그래도 한번 만든 함수를 필요할때만 새로 만들고 재사용하는 것은 중요하다!

  • why 함수 재사용??

    우리가 나중에 컴포넌트에서 props가 바뀌지 않았다면 Virtual DOM에 새로 렌더링하는 것 조차 하지않고 컴포넌트의 결과물을 재사용하는 최적화 작업 할건데, 이때 함수 재사용 필수다

  • 함수가 매번 재선언되면 하위 컴포넌트는 넘겨 받은 함수가 달라졌다고 인식한다.

  • 컴포넌트는 자신의 state가 변경되거나, 부모에게서 받는 props가 변경되었을 때마다 리렌더링된다. (심지어 하위 컴포넌트에 최적화 설정을 해주지 않으면 부모에게서 받는 props가 변경되지 않았더라도 리렌더링되는게 기본이다 ) 이게 싫으면 => React.memo()

  • 함수는 오로지 자기 자신만이 동일하기 때문에 상위 컴포넌트에서 callback함수를 (같은 함수더라도 ) 재선언한다면 props로 callback 함수를 넘겨 받는 하위 컴포넌트 입장에서는 props가 변경되었다고 인식한다.

    바뀐 부분

    import React, { useRef, useState, useMemo, useCallback } const onCreate = useCallback(() => {
      const user = {
        id: nextId.current,
        username,
        email
      };
      setUsers(users.concat(user));
    
      setInputs({
        username: '',
        email: ''
      });
      nextId.current += 1;
    }, [users, username, email]);
    
      const onRemove = useCallback(
      id => {
        // user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
        // = user.id 가 id 인 것을 제거함
        setUsers(users.filter(user => user.id !== id));
      },
      [users]
    );
    const onToggle = useCallback(
      id => {
        setUsers(
          users.map(user =>
            user.id === id ? { ...user, active: !user.active } : user
          )
        );
      },
      [users]
    );
  
  
## 주의할 점
- 함수 안에서 사용하는 상태 혹은 props 가 있다면 꼭, deps 배열안에 포함시켜야 된다는 것 입니다. 
- 만약에 deps 배열 안에 함수에서 사용하는 값을 넣지 않게 된다면, 함수 내에서 해당 값들을 참조할때 가장 최신 값을 참조 할 것이라고 보장 할 수 없습니다.
- props 로 받아온 함수가 있다면, 이 또한 deps 에 넣어주어야 해요.
- 사실, useCallback 은 useMemo 를 기반으로 만들어졌습니다. 다만, 함수를 위해서 사용 할 때 더욱 편하게 해준 것 뿐이지요. 이런식으로도 표현 할 수 있습니다.
  
  
profile
초록색 귤이 노랑색으로 익어가듯, 실력이 익어가기 위해 노력하는 개발자 lahee입니다. 프론트엔드 개발자를 목표로 성장하고 있습니다.

1개의 댓글

comment-user-thumbnail
2022년 1월 14일

우아! 정말 좋은 정보 감사합니다 :)

답글 달기