최적화3 - useCallback

조뮁·2022년 11월 8일

React

목록 보기
18/34

컴포넌트 최적화

  1. 최적화할 컴포넌트 대상 찾기
  • React developer tools -> Highlight updates when components render 사용
  • 일기 리스트 삭제 시, 일기 작성 폼도 리랜더됨 -> 일기 작성폼 최적화 하기!

DiaryEditor 최적화하기

// React.memo 활용하여 컴포넌트 최적화
export default React.memo(DiaryEditor);
  • DiaryEditor 함수는 너무 길기때문에, export default로 컴포넌트를 내보낼 때 React.memo로 묶어서 사용
const DiaryEditor = ({ onCreate }) => {
  useEffect(() => {
    console.log("DiaryEditor 랜더");
  });
  • 페이지 로딩 시, DiaryEditor는 2번 랜더됨
    • App컴포넌트에서 data state의 초기값 = [] (랜더링 1)
    • App 컴포넌트 내 getData()함수에서 데이터를 받아오면 data state가 업데이트 되어 App 컴포넌트 리랜더 -> DiaryEditor는 App 컴포넌트에서 onCreate함수 전달받음 -> DiaryEditor 컴포넌트도 리랜더 (랜더링 2)
    • onCreate()가 갖고 있는 데이터형 : 객체 이기 때문에, 얕은비교가 일어나서 React.memo를 사용했음에도 계속 다시 호출됨
    • 랜더링이 계속 발생되는 원인 : onCreate()
  • 같은 이유로 일기 삭제 시에도, onCreate()가 재생성되기 때문에 DiaryEditor 랜더됨

주의 : onCreate()에 React.useMemo 사용 불가능

  • 우리가 원하는 기능 : onCreate()를 재생성하지 않고 그대로 DiaryEditor에 전달하는 것
  • useMemo()는 '값'을 반환하기 때문에, onCreate() 함수 자체를 전달할 수 없다.

useCallback

리액트 공식문서 참고
https://ko.reactjs.org/docs/hooks-reference.html#usecallback

const memoizedCallback = useCallback(
  () => {// 첫번째 인자 : callback 함수
    doSomething(a, b);
  },
  [a, b], // 두번째 인자 : dependency array
);
  • 메모이제이션 된 콜백을 반환
  • dependency arr의 인자가 변하지 않으면, 첫 번째 인자로 전달받은 메모이제이션 된 callback 함수를 계속 재사용할 수 있음
// App.js
  // 일기 생성 함수
  const onCreate = useCallback((author, content, emotion) => {
    const created_date = new Date().getTime();
    const newItem = {
      author,
      content,
      emotion,
      created_date,
      id: dataId.current, // dataId의 초기값 = 0 , 현재 dataId의 값을 가져옴
    };
    dataId.current += 1; // dataId 사용 후, 현재값을 1씩 추가해주기
    setData([newItem, ...data]);
  }, []);
  • 함수 재사용을 위해 useCallback으로 감싸기
  • dependency arr를 빈배열로 전달해서, 최초 mount 시에만 한 번만 만들고 그 다음부터는 함수 재사용
  • DiaryEditor 컴포넌트가 mount 시 한 번만 랜더되고, 일기 리스트를 삭제하지 않아도 다시 랜더되지 않음

  • 일기 생성 시, 모든 일기 리스트가 사라지고 새로 작성한 일기만 저장됨
  • dependency arr를 빈 배열로 전달했기 때문!
    • onCreate()는 컴포넌트 mount 시 최초 한 번만 생성되기 때문에, 그 당시 data state의 값인 [ ]를 저장하고 있음.
    • 컴포넌트가 재생성 될 때, 함수도 재호출 되는 이유 = 현재의 state값 (...data)를 참조하기 위함
  • dependency array에 data를 넣어준다면?
    • data state가 변화할 때 onCreate() 함수의 재생성을 방지하려고 useCallback을 사용했던 것인데...
      -> 함수형 업데이트 사용

함수형 업데이트

setData()전달 시, 새로운 state의 값으로 함수를 전달하는 것

const onCreate = useCallback((author, content, emotion) => {
    const created_date = new Date().getTime();
    const newItem = {
      author,
      content,
      emotion,
      created_date,
      id: dataId.current, // dataId의 초기값 = 0 , 현재 dataId의 값을 가져옴
    };
    dataId.current += 1; // dataId 사용 후, 현재값을 1씩 추가해주기
    setData([newItem, ...data]);
  }, []);
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을 추가한 ...date를 return하는 callback 함수 전달
    setData((data)=>[newItem, ...data]);
  }, []);
  • dependency array를 비워놔도, detData의 인자(data)를 통해 최신의 state를 참고할 수 있음

  • 페이지 로딩, 일기 삭제 시에도 DiaryEditor 한 번만 랜더됨

0개의 댓글