Hook - useMemo, React.memo, useCallback

  • 연산 최적화 할 때 사용
  • Memoization : 이미 계산 해 본 연산 결과를 기억 해 두었다가 동일한 계산을 시키면, 다시 연산하지 않고 기억(캐싱) 해 두었던 데이터를 반환 시키게 하는 방법

🔽 useMemo

일반 함수로 작업시

//App=()=>{}안에 들어있으며, useEffect(()=>{},[])사용 중

const getDiaryAnalysis = () => {
    const goodCount = data.filter((item)=>item.emotion >= 3).length;
    const badCount = data.length - goodCount;
    const goodRatio = (goodCount / data.length) * 100;
  
    return {goodCount, badCount, goodRatio}
  }

const {goodCount, badCount, goodRatio} = getDiaryAnalysis()

//...결과값 사용 예시 생략
  • 첫 로드시 2번 실행됨
    1. App컴포넌트가 처음 Mount될 때
    2. useEffect에서 사용한 getData()가 실행되면서 데이터를 가져온 후 App컴포넌트가 리렌더 될 때
  • 다른 state를 수정 시에도 App컴포넌트가 리렌더되어 한번 더 작동하기에 불필요한 작업을 다시 하는건 비효율적임

useMemo hook 사용

  • useMemo(()=>{},[]) 사용하여 필요 할 때에만 사용 가능
//App=()=>{}안에 들어있으며, useEffect(()=>{},[])사용 중

const getDiaryAnalysis = useMemo(() => {
    const goodCount = data.filter((item)=>item.emotion >= 3).length;
    const badCount = data.length - goodCount;
    const goodRatio = (goodCount / data.length) * 100;
  
    return {goodCount, badCount, goodRatio}
  }, [data.length])

const {goodCount, badCount, goodRatio} = getDiaryAnalysis

//...결과값 사용 예시 생략
  • getDiaryAnalysis는 더이상 함수호출이 아닌 useMemo내의 콜백함수의 return값을 바로 가진다.
  • [ data.length ]가 바뀔 때에만 useMemo를 재실행한다.



🔽 React.memo

  • 고차컴포넌트(HOC, Higher Order Component)
    => 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수
  • 컴포넌트 재 사용 : 부모컴포넌트가 재렌더 될 때 재렌더가 필요하지 않은 자식컴포넌트도 재렌더 되는걸 방지
    => 부모컴포넌트의 스테이트가 바뀔 때 재렌더방지용
    => 본인의 스테이트가 바뀔 때는 재렌더 됨
  • 함수형 컴포넌트에 업데이트 조건을 걸어줌 : 자식컴포넌트는 업데이트 조건에 따라서만 렌더링 되게 함
  • 기본 사용 문법
    const MyComponent = React.memo(function MyComponent(props) {
      /* props를 사용하여 렌더링 */
    });
    • 동일한 props로 동일한 결과를 렌더링 한다면 재실행 하지 않는다!
      => 마지막으로 렌더링 한 결과를 재사용함

예제
const CounterA = React.memo(({count}) => {
    useEffect(()=>{
        console.log(`CounterA Update - count: ${count}`)
    })

    return <div>{count}</div>
})

const CounterB = React.memo(({obj})=>{
    useEffect(()=>{
        console.log(`CounterB Update - count: ${obj.count}`)
    })

    return <div>{obj.count}</div>
})

const OptimizeTest = () => {
    const [count, setCount] = useState(1);
    const [obj, setObj] =useState({
        count: 1,
    });

    return <div style={{ padding: 50 }}>
        <div>
            <h2>Counter A</h2>
            <CounterA count={count}/>
            <button onClick={() => setCount(count)}>A button</button>
        </div>
        <div>
            <h2>Counter B</h2>
            <CounterB obj={obj}/>
            <button onClick={() => setObj({
                count: obj.count,
            })}>B button</button>
        </div>
    </div>
  • CounterA는 prop으로 동일한 값을 줘서 재실행 되지 않음
  • CounterB는 prop으로 객체를 줘서 얕은비교(객체 !== 객체)가 됨으로 재실행 됨
    => areEaual를 비교함수로 사용하여 한번 더 비교를 해줘야 함
    const areEqual = (prev, next) => {
      if(prev.obj.count === next.obj.count) {
          return true
      }
      return false
    }
    const MemoizedCounterB = React.memo(CounterB, areEqual)
    => obj.count의 값이 동일하면 true를 반환하기에 재실행되지 않음

🔽 useCallback

  • 메모이제이션된 콜백을 반환 : useMemo와 비슷하게 동일한 작업을 방지하게 해주는 것
    => useMemo는 값을 반환해서 전달하지만, useCallback은 함수 자체를 전달
    => useCallback(fn, deps)useMemo(() => fn, deps) 와 같다
  const onCreate = useCallback((author, content, emotion) => {
    const created_date = new Date().getTime();
    const newItem = {
      author,
      content,
      emotion,
      created_date,
      id: dataId.current,
    }
    dataId.current += 1;
    setData((data) => [newItem, ...data])
  },[])
  • Mount될 때 실행되는것을 마지막실행으로 기억하고 동일한 작업을 해야할 때 마지막실행을 가져다 씀
  • 함수재생성 하면서 최신state 유지 : setData()에서 현재 data값을 담아 함수를 전달해줌으로써 onCreat가 변동사항 있을때에도 data의 유지가 가능


공부하며 정리&기록하는 ._. 씅로그

좋은 웹페이지 즐겨찾기