useCallback으로 함수 재사용

soo's·2023년 4월 16일
0

React Study

목록 보기
14/14
post-thumbnail

0. 왜 쓰나요

부모 컴포넌트에서 자식 컴포넌트로 Prop으로 함수를 줄 때가 있다. 이때, 부모 컴포넌트가 리렌더링 되면 부모가 넘겨준 Prop 함수가 부모 안에 있으니까 얘도 계속해서 재생성되는데, 이게 불필요한 경우가 있다.

function ParentComponent() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <ChildComponent onClick={handleClick} />
    </div>
  );
}

여기서 지금 부모에서 자식한테 handleClick이라는 함수를 prop으로 전달하고 있다. 얘는 걍 카운트를 1씩 증가시키는 함수인데, 부모가 렌더링 될때마다 당연히 이 함수도 재생성된다 -> 불필요한 함수 재생성으로 인해 성능이 낭비되고 있음 ❗️ 따라서 useCallback으로 이 녀석 성능최적화 해주자.

1. useCallback 사용

const memoizedCallback = useCallback(
  () => {
    // callback 함수 내용
  },
  [/* dependency 배열 */],
);

모양은 이렇게 생겼다.
React의 useCallback hook은 성능 최적화를 위한 hook 중 하나.
이전에 생성된 콜백 함수를 메모이제이션하여 동일한 함수를 재사용-> 이를 통해, 불필요한 함수 생성과 렌더링을 줄이고, 컴포넌트의 성능 향상

useCallback은 콜백 함수를 메모이제이션하고, 의존성(dependency)이 변경될 때만 함수를 재생성함. 의존성 배열을 제공하지 않으면, 콜백 함수는 컴포넌트가 렌더링될 때마다 새로 생성됨 주의바람!

따라서 0.왜 쓰나요에서 봤던 부모 컴포넌트의 prop으로 전달한 함수의 최적화를 아래와 같이 useCallback을 이용해서 실현할 수 있음

function ParentComponent() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <ChildComponent onClick={handleClick} />
    </div>
  );
}

function ChildComponent({ onClick }) {
  return (
    <button onClick={onClick}>Increment Count</button>
  );
}

handleClick 함수가 count에 의존하므로, useCallback hook을 사용하여 count가 변경될 때만 함수를 재생성!


2. 주의할 부분


function onCreate = ({name, content}) => {
	const newItem = {
    	name,
        content,
    }
    setData([newItem, ...data])
}

onCreate 함수가 부모에서 자식으로 넘겨주는 prop임
부모의 useState()로 만들어낸 data와 setData를 이용해서 새로 생성된 item을 저장하는 함수임
그런데, 부모의 data가 변경되면 -> setData에 의해 리렌더됨 -> onCreate(부모에서 만든 함수임)도 재생성
이 문제를 해결하려고 useCallback을 써서 아래와 같이 코드를 짬


function onCreate = useCallback(({name, content}) => {
	const newItem = {
    	name,
        content,
    }
    setData([newItem, ...data])
}, []);

이 상태에서 onCreate(onClick에 연결되어있음)가 실행되면 기존의 data + 새로운 item이 추가된 배열이 렌더링 되어야 하는데, 그게 아니라 걍 새로운 item만 덩그러니 있음

왜요 ㅋㅋ;

이유는 바로 의존성 배열에 아무런 값도 주지 않았기때문에 걍 부모의 data의 초기값인 빈 배열에서 data가 시작되고, onCreate 눌렀을때 setData에 의해서 바로 [] -> item 추가 로직이 되는거임
그러면 이걸 해결하려면 의존성 배열에 data를 넣어서 [data] 이렇게 작성하면 되지 않음?

저렇게 하면 data가 변경될 때마다 onCreate가 재생성되니까 의미가 걍 useCallback 쓰는 의미가 없어짐. 나는 data가 변경되더라도(data는 부모꺼라서 다이어리 수정이든 삭제든 기타 등등이 다 data 변경임) create랑 관련된거 아니면 굳이 재생성하지 않길 원함.

그러면 함수형 업데이트를 해라!

그게 머냐면 setData에 setData([newItem, ...data]) 그냥 이렇게 적는게 아니라 setData((data) => [newItem, ...data]) 이렇게 적어주는 것임
차이가 먼가요... 한다면 함수형 업데이트로 setData를 작성하게되면 인자를 통해서 가장 최신의 state를 참조할 수 있게 됨! 따라서 의존성 배열을 걍 비워놔도 가장 최신의 state를 참조해서 거기에 item을 추가하게됨! 해결.

☠️ 결론

주로 부모에서 Prop으로 넘겨주는 함수를 최적화 할 때 useCallback을 사용하는 것 같음. 나도 prop 넘겨주는 애들은 부모가 렌더링될 때마다 재생성되지 않게 useCallback으로 관리해야겠음
그리고 setData 쓸 때는 굳이 현재의 값을 참조할 필요가 없는 경우가 아니라면 다 함수형 업데이트로 작성해야겠음!

0개의 댓글