패스트 캠퍼스 MGS 3기 - 5월 23일(useEffect, Memoization)

JY·2022년 5월 23일
0
post-thumbnail

useEffect


  • side effect를 다룰 때 사용하는 Hook이다.
  • 다음 코드는 리렌더 될 때마다 Document에 대해 이벤트 리스너가 중복으로 생성된다.
    • 이벤트 리스너가 중복으로 생성되어서 클릭 한 번에 alert이 여러 번 뜨게 되는 것


이때, useEffect를 사용해서 코드를 다음처럼 수정하면 이벤트 리스너가 최초 렌더 시 한 번만 생성된다.
만약, 의존성 배열로 [value]를 주면 value에 값이 들어갈 때마다 이벤트 리스너가 생성된다.

import React, {useState, useEffect} from 'react'

export default function Ex1() {
  const [value, setValue] = useState('');

  useEffect(() => {
    document.addEventListener('click', () => {
      alert(1);
    })
  }, [])
  console.log("컴포넌트 리렌더. 현재 스테이트: ", value);
  
  return (
    <input type="number" value={value} onChange={(e)=>{setValue(e.target.value)}} />
  )
}

🤔 Mount, Unmount

  • Mount: DOM 트리에 올라간 것
  • Unmount: 아예 DOM 트리에서 사라진 것


    로그인 누르면 <User /> Mount
    로그아웃 누르면 <User /> Unmount

1. Dependency Array


  • []: Mount 됐을 때 최초 1회 실행
  • [value]: value가 바뀔 때마다 실행
    👉 둘 다 무조건 한 번은 실행된다.

값이 바뀌는 것들을 의존성 배열에 넣을 수 있다. (prop이나 state) ref 같은 경우에는 값이 변경된다는 개념이 아니므로 넣지 않는다.

2. clean up


다음 코드에서 value에 값을 넣어도(input에 값을 입력해도) 초기에 들어있던 값('')이 alert에 나오게 된다.

useEffect 안에서 state를 사용하고 싶다면 의존성 배열로 [value] 주어야 한다. value가 변경될 때마다 실행되도록!

그런데 바뀐 value에 대해서 사용하고 싶어서 의존성 배열을 [value]로 주었는데, 이렇게 하면 value가 바뀔 때마다 이벤트 리스너가 생성돼서 쌓인다.

'123'을 입력했을 때

  • 기대한 결과: alert 1번 나타나기 (123)
  • 실제 결과: alert 4번 나타나기 (초기값 -> 1 -> 2 -> 3)

❗ 이때 필요한 것이 clean up이다.
최초로 한 번 렌더된 이후, 리렌더링 될 때
1. return문이 먼저 실행된다.
2. 그리고 바뀐 값에 대해 useEffect가 실행돼서 이벤트 리스너가 등록되는 것이다.

🤔 왜 제일 처음 실행됐을 땐 return문이 실행되지 않을까?
useEffect는 clean up 함수를 잘 들고 있다가 value가 바뀌었을 때 실행한다.
그래서 최초로 렌더되었을 때는 clean up 함수가 실행되지 않는 것이다.

3. 실습


👩‍💻 01

  • input value를 수정하면 3초 이후에 현재 value를 콘솔에 1회 출력하기
  • 3초가 경과하기 이전에 value를 수정하면 다시 3초 대기


  • setTimeout(), clearTimeout() 이용
import React, {useState, useEffect} from 'react'

export default function Ex1() {
  const [value, setValue] = useState('');
  
  useEffect(() => {
    let timer;
    if (value){
      timer = setTimeout(() => {
        console.log(value);
      }, 3000);
    }
    
    return () => {
      if(timer) {
        clearTimeout(timer);
      }
    }
  }, [value]);
  
  return (
    <>
      <input
        type="number"
        onChange={(e) => {setValue(e.target.value)}} 
      />
    </>
  );
}

🤔 리액트가 리렌더를 하기 위해 값이 바뀌었는지 판단하는 방법?
array/object(레퍼런스 타입)의 경우, 참조하는 주소가 바뀌었을 때 값이 바뀌었다고 판단한다.
따라서 의존성 배열에 레퍼런스 타입을 넣는 경우, 변경을 감지할 수 있도록 작성해야 한다. (= 참조하는 주소가 바뀌도록 해야 한다.)


👩‍💻 02

  • 실습 01 코드에서 남은 시간 출력되도록 추가하기

Memoization


  • 함수가 리렌더 되어도 다시 계산하지 않고, 값을 저장해두었다가 보여주는 것이다.
    • value가 바뀌어도 1부터 2억까지 더한 값은 변하지 않으므로 저장해둔다.
  • 주로 컴퓨팅 파워가 많이 들어가는 연산에 사용한다.


useMemo


  • useMemo를 통해서 계산된 값을 저장할 수 있다.
  • 최초로 렌더될 때는 시간이 걸리지만 그 이후로는 0초


다음 코드는 의존성 배열이 변경되었을 때 메모이제이션을 다시 수행한다.

  • 부모의 input에 값이 다시 입력되면 [props.max]가 변경되었으므로 다시 계산한다.
  • 자식의 input에 값을 입력했을 땐 다시 계산하지 않는다.
  • 만약 의존성 배열을 [props.max, value]로 주면 자식 input이 바뀌었을 때도 불필요한 계산을 하게 된다.


useCallback


  • useMemo의 함수 버전이다.
  • useMemo는 콜백함수에서 return 하는 값이 메모이제이션 되는 것
  • useCallback은 콜백함수 자체가 메모이제이션 되는 것


🤔

  • 함수를 return할 땐 useMemo, useCallback 둘 다
  • 그 외엔 useMemo




참고

현업에서 대부분의 환경에서는 Memoization을 사용하진 않으므로 그냥 정말! 필요할 때 '아 이런 게 있었지~' 라고 생각할 수 있는 정도면 OK.

profile
🙋‍♀️

0개의 댓글