react 기본기 useCallback

미마모코딩·2022년 10월 14일
0

리액트 기본기

목록 보기
6/6
post-thumbnail
post-custom-banner

useCallback은 useMemo처럼 기본적으로 Memoization을 사용한다.

useCallback과 useMemo의 차이

useCallback은? useCallback(()=>{return value},[item])
위에 굵은 글씨로 칠한 부분 즉 콜백함수 자체를 memoization을 한다.

useMemo는? useMemo(()=>{return 10},[item])
리턴값을 memoization을 한다.

useCallback을 이해하기 위해선 몇가지 배경지식이 필요하다.

1.함수는 객체다. (일급객체 참고)

  1. 함수 컴포넌트는 상태변화시 다시 렌더링이 된다.

3.초기화가 된다는건 새로운 객체가 다시 만들어지고, 메모리 주소도 새로 생성 된다.

4.즉 렌더링 전 변수와, 렌더링 후 변수가 다르다. {A} === {A} // FALSE

5.useEffect에서 의존성 배열은, 객체 구분을 하지 못하고, 렌더링이 계속 일어난다.


import React, { useEffect, useState } from "react";

const App = () => {
  const [number, setNumber] = useState(0);

  function someFunction() {
    console.log(`someFunction:number:${number}`);
    return;
  }

  useEffect(() => {
    console.log("someFun이 변경되었습니다.");
  }, [someFunction]);
  return (
    <div>
      <input
        type="number"
        value={number}
        onChange={(e) => setNumber(e.target.value)}
      ></input>
      <br />
      <button onClick={someFunction}>call someFunction</button>
    </div>
  );
};

export default App;

위 코드를 보면 someFucntion함수를 useEffect의 의존성배열에 넣어두었다.

someFuction의 함수 내용이 바뀌지 않는한 useEffect는 다시 호출되지 않을것이다. 라고 생각한다면 결국은 오답이다.

우리는 스테이트의 값이 바뀐다면 랜더가 일어나고 랜더가 일어나면 컴포넌트 내부의 변수들이 초기화된다.

또한 함수는 객체이기 때문에 객체를 참조하던 메모리의 주소값도 바뀌게된다.

위 배경지식에서 언급한 4.즉 렌더링 전 변수와, 렌더링 후 변수가 다르다. {A} === {A} // FALSE 의 문제로 객체 자체가 변경되었다고 판단해 state가 변경될 때마다 함수자체가 계속 바뀐다고 판단하는것이다.

그렇다면 어떻게 개선 할 수 있을까?

import React, { useEffect, useState, useCallback } from "react";

const App = () => {
  const [number, setNumber] = useState(0);

  const someFunction = useCallback(() => {
    console.log(`someFunction:number:${number}`);
    return;
  }, []);

  useEffect(() => {
    console.log("someFun이 변경되었습니다.");
  }, [someFunction]);
  return (
    <div>
      <input
        type="number"
        value={number}
        onChange={(e) => setNumber(e.target.value)}
      ></input>
      <br />
      <button onClick={someFunction}>call someFunction</button>
    </div>
  );
};

export default App;

위와 같이 someFunction을 useCallback으로 감싸주면 함수객체가 다시 최초한번 빼고는 다시 생성되지 않아 useEffect에서는 객체의 변화감지를 찾아내지 못하기때문에 useEffect내부의 코드블록이 실행되지 않는다.

문제점

우리는 함수객체를 memoization을 통해 다시 새롭게 초기화하지 않게 만들었다.

하지만 함수객체의 내용에선 console.log(someFunction:number:${number}); 스테이트의 값을 참조하고있다.

우리가 5로 값을 늘리고 출력을해도 memoization 했을때의 state값이 0이기때문에 0을 바라보고있다.

새로운 개선방법

import React, { useEffect, useState, useCallback } from "react";

const App = () => {
  const [number, setNumber] = useState(0);

  const someFunction = useCallback(() => {
    console.log(`someFunction:number:${number}`);
    return;
  }, [number]);

  useEffect(() => {
    console.log("someFun이 변경되었습니다.");
  }, [someFunction]);
  return (
    <div>
      <input
        type="number"
        value={number}
        onChange={(e) => setNumber(e.target.value)}
      ></input>
      <br />
      <button onClick={someFunction}>call someFunction</button>
    </div>
  );
};

export default App;

바로 someFunction의 의존성배열에 number가 바뀔때마다 새롭게 memoization을 하게끔 만드는 것이다.

이렇게 되면 number state의 값을 참조 할 수 있게된다.

number state가 바뀔때마다 useCallback의 함수객체를 새로운 메모리주소로 memoization하기때문에 number가 변경되면 useEffect도 useCallback이 변경했다고 판단하여 useEffect가 호출된다.

useMemo와 useCallback은 비슷한듯 비슷하지 않기때문에 특징을 잘 기억하고 함수가 객체라는 사실에 동작이 원하는대로 되지 않을 수 있다.

항상 경계하면서 올바른 상황에 사용 할 수 있게 공부하길 바란다.

post-custom-banner

0개의 댓글