열렬히.뛰기

함수형 컴포넌트와 hook (2)

웹 프로그래밍 > React > 리액트의 기초 > 함수형 컴포넌트와 hook (2)

useCallback

useMemo()와 비슷하나, 값 대신 함수를 반환

언제 쓰나?

  1. 자식 컴포넌트에 콜백을 전달할 때 : 자식 컴포넌트가 React.memo나 shouldComponentUpdate를 사용하여 최적화된 경우, 콜백 함수가 변경되지 않도록 해서 불필요한 리렌더링을 방지할 수 있습니다.
  2. 성능 최적화가 필요할 때 : 함수 재생성이 비싼 연산이거나, 함수가 종속된 값이 자주 변경되지 않는 경우
  3. 의존성 배열에 기반한 재생성 : 특정 값이 변경될 때만 함수를 재생성하고 싶을 때 유용

예시 코드

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

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

    // handleClick 함수는 count가 변경될 때만 재생성됨
    const handleClick = useCallback(() => {
        console.log(`Count: ${count}`);
    }, [count]);

    return (
        <div>
            <p>Count: {count}</p>
            <p>Other Value: {otherValue}</p>
            <button onClick={() => setCount(count + 1)}>
                Increment Count
            </button>
            <button onClick={() => setOtherValue(otherValue + 1)}>
                Increment Other Value
            </button>
            <ChildComponent onClick={handleClick} />
        </div>
    );
}

function ChildComponent({ onClick }) {
    console.log('Child component rendered');
    return (
        <div>
            <button onClick={onClick}>Click me</button>
        </div>
    );
}

export default ParentComponent; 

좀 복잡한 코드이지만, 한번 잘 살펴보자.

  1. count가 변경될 때 useCallback()은 handleClick() 함수를 생성
  2. handleClick()은 ChildComponent의 props인 onClick으로 들어가고 있음
  3. 따라서 Click me를 누를 때, handleClick()이 실행.

즉, ParentComponent가 리렌더링 된다고 ChildComponent가 무조건 리렌더링 되지 않음.

따라서 성능상의 이점을 얻을 수 있음.

useRef

레퍼런스를 사용하기 위한 훅

레퍼런스 : 특정 객체나 요소를 참조하는 것. (객체지향에서의 그 레퍼런스처럼 생각)

예시 코드

javascript
import React, { useEffect, useRef } from 'react';

function LoginForm() {
  const emailInput = useRef(null);

  useEffect(() => {
    emailInput.current.focus();
  }, []);

  return (
    <form>
      <label>
        Email:
        <input ref={emailInput} type="email" />
      </label>
      <label>
        Password:
        <input type="password" />
      </label>
      <button type="submit">Login</button>
    </form>
  );
}

export default LoginForm;
  1. useRef 훅을 호출하여 emailInput이라는 변수를 생성한다. 이 변수는 초기 값으로 null을 가진 ref 객체를 반환한다.

    이 ref 객체는 .current를 가지고 있으며, 이는 나중에 참조할 DOM 요소를 가리킴.

  2. input 요소에 ref 속성을 설정하여, 이 요소가 emailInput 객체의 .current 속성에 할당되도록 한다.

  3. useEffect()가 걸리면, emailInput.current.focus()가 자동으로 실행.

    사용자가 로그인 폼 페이지에 접속하면 이메일 입력 필드가 자동으로 입력을 받을 수 있게 준비되어 있다.

훅의 규칙

  1. 무조건 최상위 레벨에서만 호출해야 한다.
    • 즉 반복문, 중첩문, 조건문 안에서는 실행하면 안된다.
    • 컴포넌트가 렌더링 될 때마다 매번 같은 순서로 호출되어야 한다.
  2. 리액트 함수 컴포넌트에서만 훅을 호출해야 한다.
    • 일반적인 자바스크립트 함수에서 훅을 호출하면 안된다.

사용자 정의 훅

  • 특정 로직을 컴포넌트에서 분리하여 독립적으로 테스트하고 재사용할 수 있다.
  • 이를 통해 코드의 가독성과 유지보수성을 크게 향상시킬 수 있다.
  • 규칙 : 반드시 use로 시작해야 한다. ex. useWindowSize, useTimer

예시 코드

javascript
import { useState, useEffect } from 'react';

// 커스텀 훅: useWindowSize
function useWindowSize() {
  // width와 height를 상태로 정의
  const [size, setSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight,
  });

  // 윈도우 크기 변경을 처리하는 핸들러
  const handleResize = () => {
    setSize({
      width: window.innerWidth,
      height: window.innerHeight,
    });
  };

  useEffect(() => {
    // 컴포넌트가 마운트되었을 때 리스너 등록
    window.addEventListener('resize', handleResize);

    // 클린업 함수: 컴포넌트가 언마운트되거나 리스너가 변경될 때 리스너 제거
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []); // 빈 배열을 두 번째 인자로 주어, 한 번만 실행되도록 설정

  return size;
}

export default useWindowSize;
  1. 커스텀 훅 정의
  • useWindowSize 훅은 useState와 useEffect를 사용하여 윈도우의 크기를 추적.
  • useState를 사용하여 size 상태를 정의하고, 초기값으로 현재 창의 크기를 설정.
  • handleResize 함수는 창 크기가 변경될 때 상태를 업데이트.
  • useEffect는 컴포넌트가 마운트될 때 창 크기 변경 이벤트 리스너를 등록하고, 컴포넌트가 언마운트될 때 리스너를 제거하는 클린업 함수를 반환.
javascript
import React from 'react';
import useWindowSize from './useWindowSize';

function WindowSizeComponent() {
  const size = useWindowSize(); // 커스텀 훅 호출

  return (
    <div>
      <h1>Window Size</h1>
      <p>Width: {size.width}px</p>
      <p>Height: {size.height}px</p>
    </div>
  );
}

export default WindowSizeComponent;
  1. 커스텀 훅 사용:
  • WindowSizeComponent 컴포넌트는 useWindowSize 훅을 호출하여 현재 창의 크기를 가져온다.
  • size 객체는 width와 height 속성을 가지며, 이를 JSX에서 출력하여 현재 창 크기를 표시.