본문 바로가기
React

[React]useCallback

by jyee 2024. 9. 3.
728x90
반응형

✔️useCallback이란?

 

  • useCallback은 함수를 메모이제이션합니다. 주어진 종속성 배열이 변경되지 않으면, 동일한 함수 인스턴스를 반환합니다. 이는 컴포넌트가 불필요하게 다시 렌더링되는 것을 방지하는 데 도움을 줍니다.
  • 언제 사용: 컴포넌트가 자주 다시 렌더링되면서 동일한 함수가 다시 생성되는 것을 피하고 싶을 때, 특히 자식 컴포넌트에 콜백 함수를 props로 전달할 때 사용합니다.

usememo랑 너무 헷갈렸는데 둘 다 비슷하긴 하지만 usememo는 특정 결과값을 재사용하는 반면, usecallback은 특정 함수를 새로 만들지 않고 다시 재사용하는 것이다. 

 1. useCallback의 구조

import React, { useState, useCallback } from 'react';

function Button({ onClick, label }) {
  console.log('Button rendered!');
  return <button onClick={onClick}>{label}</button>;
}

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

  // useCallback을 사용하여 콜백 함수를 메모이제이션
  const handleClick = useCallback(() => {
    setCount(prevCount => prevCount + 1);
  }, []); // 종속성 배열이 비어있으므로 처음에만 생성되고 이후에는 재사용됨

  return (
    <div>
      <Button onClick={handleClick} label="Increment" />
      <p>Count: {count}</p>
    </div>
  );
}

export default ParentComponent;

 

ParentComponent가 다시 렌더링되더라도 handleClick 함수는 새로 생성되지 않으며, 동일한 함수 인스턴스가 자식 컴포넌트 Button에 전달됩니다. 이로 인해 Button 컴포넌트는 onClick prop이 변경되지 않았다고 판단해 불필요한 렌더링을 방지할 수 있습니다.

 

 

function ParentComponent() {
  const [count, setCount] = useState(0);
  
  const handleClick = useCallback(() => {
    setCount(prevCount => prevCount + 1);
  }, []);
  
  return (
    <div>
      <ChildComponent onClick={handleClick} />
    </div>
  );
}
 
function ChildComponent({ onClick }) {
  return (
    <button onClick={onClick}>Click me</button>
  );
}

handleClick 함수가 ParentComponent 컴포넌트에서 생성되어 ChildComponent 컴포넌트에 props로 전달.

만약 useCallback을 사용하지 않으면 ParentComponent가 리렌더링 될 때마다 handleClick 함수가 새로 생성됩니다. 이 경우 ChildComponent는 onClick props가 변경되었으므로 불필요한 리렌더링이 발생하게 되고 이를 방지하기 위해 useCallback을 사용하여 handleClick 함수를 메모이제이션함

 

💡 메모이제이션 이란?
이전에 계산한 값을 메모리에 저장해두고, 동일하게 다시 사용할 수 있는 곳에서 재사용하여 반복적으로 발생하는 계산의 리소스를 줄이는 기법

 

 

언제 useCallback을 사용하는 것이 최적화에 도움이 될까?

  1. 자식 컴포넌트에 함수를 전달할 때
    • 문제: 자식 컴포넌트가 부모 컴포넌트로부터 받은 함수가 변경될 때마다 다시 렌더링될 수 있습니다. 특히, 자식 컴포넌트가 React.memo로 최적화되어 있거나, shouldComponentUpdate 메소드를 사용하는 경우, 자식 컴포넌트는 props가 변경될 때만 업데이트됩니다.
    • 해결: useCallback을 사용하여 함수의 인스턴스를 메모이제이션하고, 자식 컴포넌트가 이전과 동일한 함수를 받을 수 있도록 합니다. 이렇게 하면 불필요한 렌더링을 방지할 수 있습니다.
import React, { useState, useCallback } from 'react';

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

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

  return <ChildComponent onClick={handleClick} />;
}

const ChildComponent = React.memo(({ onClick }) => {
  console.log('ChildComponent re-rendered');
  return <button onClick={onClick}>Click me!</button>;
});

 

 

2.자주 호출되는 함수가 있을 때

  • 문제: 함수가 자주 호출되거나 복잡한 로직을 포함하고 있다면, 매번 새로 생성되면 성능에 영향을 줄 수 있습니다.
  • 해결: useCallback을 사용해 함수를 메모이제이션하면, 함수가 동일한 인스턴스를 유지하게 되어 불필요한 재생성을 막을 수 있습니다.
import React, { useState, useCallback } from 'react';

function ExpensiveComponent() {
  const [value, setValue] = useState(0);

  const expensiveFunction = useCallback(() => {
    // 복잡한 계산
    console.log('Expensive function called');
  }, []);

  return (
    <div>
      <button onClick={expensiveFunction}>Run Expensive Function</button>
      <p>Value: {value}</p>
    </div>
  );
}

 

3. useEffect와 같이 사용해 컴포넌트 최적화 하는 경우

import React, { useCallback, useEffect, useState } from "react";
 
function ExampleComponent(props) {
  const [data, setData] = useState([]);
 
  const fetchData = useCallback(async () => {
    const response = await fetch("https://example.com/data");
    const data = await response.json();
    setData(data);
  }, []);
 
  useEffect(() => {
    fetchData();
  }, [fetchData]);
 
  return (
    <div>
      {data.map((item) => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  );
}
 
export default ExampleComponent;
728x90
반응형

'React' 카테고리의 다른 글

[React] Props로 전달되는 문자열 줄바꿈 하기  (0) 2024.09.26
[React]페이지 이탈 시 확인 모달창 띄우기  (0) 2024.09.13
[React] useMemo  (2) 2024.09.03
[React] useContext api  (0) 2024.08.27
[React]useReducer  (0) 2024.08.20