sohyeon kim

[React] 리액트 공식문서 정리 4(5) : 탈출구 - 커스텀 Hook 으로 로직 재사용하기 본문

React

[React] 리액트 공식문서 정리 4(5) : 탈출구 - 커스텀 Hook 으로 로직 재사용하기

aotoyae 2024. 12. 12. 14:54
728x90
반응형

 

 

💡 리액트 공식문서 정리 : 탈출구 - 커스텀 Hook 으로 로직 재사용하기

React 는 useState, useContext, useEffect 와 같은 Hook 들이 내장되어 있다.

때로는 데이터를 가져오거나 사용자가 온라인 상태인지 여부를 추적하는 등 좀 더 구체적인 목적을 가진 Hook 을 원할 수 있다.

이를 위해 애플리케이션의 필요에 알맞는 자신만의 커스텀 Hook 을 생성할 수 있다.

앱이 성장함에 따라 이미 작성한 커스텀 Hook 을 재사용할 수 있으므로 직접 작성하는 Effect 의 수가 줄어들 것이다.

 

1. 커스텀 Hook : 컴포넌트간 로직 공유하기

네트워크에 크게 의존하는 앱을 개발 중이라고 가정하자. 사용자가 앱을 사용하던 중 네트워크가 갑자기 사라진다면, 사용자에게 경고하고 싶을 것이다. 이런 경우 컴포넌트에는 다음 두 가지가 필요하다.

  1. 네트워크가 온라인 상태인지 아닌지 추적하는 하나의 state
  2. 전역 online, offline 이벤트를 구독하고, 이에 맞춰 state 를 업데이트하는 effect

이 두 가지 요소는 컴포넌트가 네트워크 상태와 동기화되도록 한다.

import { useState, useEffect } from 'react';

export default function StatusBar() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    function handleOffline() {
      setIsOnline(false);
    }
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);
    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  return <h1>{isOnline ? '✅ 온라인' : '❌ 연결 안 됨'}</h1>;
}

 

네트워크를 껐다 키면, 그에 따라 StatusBar 가 업데이트 된다.

이제 다른 컴포넌트에서 같은 로직을 또 사용한다고 해보자. 네트워크가 꺼졌을 때, '저장' 대신 '재연결 중..' 을 보여주는 비활성화된 저장 버튼을 구현하고 싶다고 가정해 보자.

이를 구현하기 위해, 앞서 사용한 isOnline state 와 effect 를 SaveButton 안에 복사할 수 있다.

import { useState, useEffect } from 'react';

export default function SaveButton() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
   //...
  }, []);

  function handleSaveClick() {
    console.log('✅ 진행사항 저장됨');
  }

  return (
    <button disabled={!isOnline} onClick={handleSaveClick}>
      {isOnline ? '진행사항 저장' : '재연결 중...'}
    </button>
  );
}

 

네크워크를 끄고 켰을 때, 컴포넌트는 잘 동작하지만 로직이 중복되는 점이 아쉽다.

두 컴포넌트가 다른 UI 를 표시하면서, 두 컴포넌트 사이의 로직을 재사용하고 싶다.

 

1️⃣ 컴포넌트로부터 커스텀 Hook 추출하기

useState, useEffect 와 비슷한 내장된 useOnlineStatus Hook 이 있다고 상상해 보자.

그럼 두 컴포넌트를 단순화할 수 있고, 중복을 제거할 수 있다.

function StatusBar() {
  const isOnline = useOnlineStatus();
  return <h1>{isOnline ? '✅ 온라인' : '❌ 연결 안 됨'}</h1>;
}

function SaveButton() {
  const isOnline = useOnlineStatus();

  function handleSaveClick() {
    console.log('✅ 진행사항 저장됨');
  }

  return (
    <button disabled={!isOnline} onClick={handleSaveClick}>
      {isOnline ? '진행사항 저장' : '재연결 중...'}
    </button>
  );
}

 

이제 useOnlineStatue 함수를 정의하고, 앞서 작성한 컴포넌트들의 중복 코드를 바꾸자.

함수의 마지막에 isOnline 을 반환하면 컴포넌트가 그 값을 읽을 수 있게 해준다.

function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    function handleOffline() {
      setIsOnline(false);
    }
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);
    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);
  return isOnline;
}

 

 

여기서 중요한 건, 두 컴포넌트 내부 코드가 어떻게 그것을 하는지(브라우저 이벤트 구독하기)보다

그들이 무엇을 하려는지(온라인 state 사용하기)에 대해 설명하고 있다는 점이다.

커스텀 Hook 을 만들어 낼 때, 브라우저 API 나 외부 시스템과 소통하는 방법과 같은 불필요한 세부 사항은 숨길 수 있으며,

컴포넌트의 코드는 목적만을 나타낼 뿐 실행 방법에 대해선 나타내지 않게 된다.

 

2️⃣ Hook 의 이름은 항상 use 로 시작해야 한다.

React 애플리케이션은 여러 컴포넌트로 만들어진다. 컴포넌트들은 내장되거나 직접 작성한 Hook 으로 만들어진다.

본인만의 Hook 을 만들려고 할 때, 다음의 작명 규칙을 준수해야 한다.

  1. React 컴포넌트의 이름은 항상 대문자로 시작해야 한다.(e.g. StatusBar)
    또한 컴포넌트는 JSX 처럼 어떻게 보이는지 React 가 알 수 있는 무언가를 반환해야 한다.
  2. Hook 의 이름은 use 뒤에 대문자로 시작해야 한다. (e.g. useState(내장 훅) or useOnlineStatus(커스텀 훅))
    Hook 들은 어떤 값이든 반환할 수 있다.

이런 규칠들은 컴포넌트를 볼 때, 어디에 state, effect 및 다른 React 기능들이 '숨어' 있는지 알 수 있게 해준다.

예시로, 만약 컴포넌트 안에 getColor() 라는 함수를 보았을 때, 해당 함수의 이름이 use 로 시작하지 않으므로 함수 안에 React state 가 있을 수 없다는 것을 확신할 수 있다. 반대로 useOnlineStatus() 함수의 경우 높은 확률로 내부에 다른 Hook 을 사용하고 있을 수 있다.

 

🧐 렌더링 중 호출되는 모든 함수는 use 접두사로 시작해야 하나?

아니다! Hook 을 호출하지 않는 함수는 Hook 일 필요가 없다.

함수가 어떤 Hook 도 호출하지 않는다면, use 를 이름 앞에 작성하는 것을 피하라. 대신 use 없이 일반적인 함수로 작성하자.

예시로 useSorted 가 Hook 을 호출하지 않는다면 getSorted 로 변경할 수 있다.

// 🔴 안 좋은 예시 : Hook을 사용하고 있지 않는 Hook.
function useSorted(items) {
  return items.slice().sort();
}

// ✅ 좋은 예시 : Hook을 사용하지 않는 일반 함수.
function getSorted(items) {
  return items.slice().sort();
}

 

 

다음 예시는 조건문 뿐만 아니라 어디든 일반 함수를 사용할 수 있다는 것을 보여준다.

function List({ items, shouldSort }) {
  let displayedItems = items;
  if (shouldSort) {
    // ✅ getSorted()가 Hook이 아니기 때문에 조건에 따라 호출할 수 있습니다.
    displayedItems = getSorted(items);
  }
  // ...
}

 

적어도 하나의 Hook 을 내부에서 사용한다면 반드시 함수 앞에 use 를 작성해야 한다. (그리고 이 자체로 Hook 이 된다.)

// ✅ 좋은 예시 : Hook을 사용하는 Hook
function useAuth() {
  return useContext(Auth);
}

 

기술적으로 이 점이 React 에 의해 강요되진 않지만, 참고하는 것이 좋다.

예시로, 지금 당장은 함수에서 어떤 Hook 도 사용하지 않지만, 추후 Hook 을 호출할 계획이 있다면 use 를 앞에 붙여 두는 것이 가능하다.

지금이든 나중이든 Hook 을 내부에서 사용할 계획이 없다면, Hook 으로 만들지 말 것!

// ✅ 좋은 예시 : 추후에 다른 Hook을 사용할 가능성이 있는 Hook
function useAuth() {
  // TODO: 인증이 수행될 때 해당 코드를 useContext(Auth)를 반환하는 코드로 바꾸기
  return TEST_USER;
}

 

3️⃣ 커스텀 Hook 은 state 그 자체를 공유하는게 아닌 state 저장 로직을 공유하도록 한다.

위 예시에선, 네트워크를 껐다 켰을 때 양쪽 컴포넌트가 함께 업데이트되었다.

하지만 그렇다고 해서 isOnline state 변수가 두 컴포넌트간 공유되었다고 생각하면 안된다.

function StatusBar() {
  const isOnline = useOnlineStatus();
  // ...
}

function SaveButton() {
  const isOnline = useOnlineStatus();
  // ...
}

// 중복된 부분을 걷어내기 전에도 동일하게 동작한다.
function StatusBar() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    // ...
  }, []);
}

function SaveButton() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    // ...
  }, []);
}

 

완전히 독립적인 두 state 변수와 effect 가 있음을 확인할 수 있다. 그들은 우리가 동일한 외부 변수(네트워크의 연결 state)를 동기화했기 때문에 같은 시간에 같은 값을 가지고 있을 뿐이다. 이걸 더 잘 표현하는 다른 예시를 살펴보자.

import { useState } from 'react';

export default function Form() {
  const [firstName, setFirstName] = useState('Mary');
  const [lastName, setLastName] = useState('Poppins');

  function handleFirstNameChange(e) {
    setFirstName(e.target.value);
  }

  function handleLastNameChange(e) {
    setLastName(e.target.value);
  }

  return (
    <>
      <label>
        First name:
        <input value={firstName} onChange={handleFirstNameChange} />
      </label>
      <label>
        Last name:
        <input value={lastName} onChange={handleLastNameChange} />
      </label>
      <p><b>Good morning, {firstName} {lastName}.</b></p>
    </>
  );
}

 

각각의 폼 입력에 반복되는 로직이 있다.

  1. state 가 존재한다. (firstName  lastName)
  2. 변화를 다루는 함수가 존재한다. (handleFirstNameChange  handleLastNameChange)
  3. 해당 입력에 대한 value 와 onChange 의 속성을 지정하는 JSX 가 존재한다.

useFormInput 커스텀 Hook 을 통해 반복되는 로직을 추출할 수 있다.

export function useFormInput(initialValue) {
  const [value, setValue] = useState(initialValue);

  function handleChange(e) {
    setValue(e.target.value);
  }

  const inputProps = {
    value: value,
    onChange: handleChange
  };

  return inputProps;
}

export default function Form() {
  const firstNameProps = useFormInput('Mary');
  const lastNameProps = useFormInput('Poppins');
  
  return (
    <>
      <label>
        First name:
        <input {...firstNameProps} />
      </label>
      <label>
        Last name:
        <input {...lastNameProps} />
      </label>
      <p><b>Good morning, {firstNameProps.value} {lastNameProps.value}.</b></p>
    </>
  );
}

 

여기서 value 라고 불리는 state 변수가 한 번만 정의된다는 것을 기억하자.

이와 달리 Fom 컴포넌트는 useFormInput 을 두 번 호출한다.

 

위 예시는 왜 두 개의 다른 state 변수를 정의하는 식으로 동작하는 지 보여준다.

커스텀 Hook 은 우리가 state 그 자체가 아닌 state 저장 로직을 공유하도록 해준다. 같은 Hook 을 호출하더라도 각각의 Hook 호출은 완전히 독립되어 있다. 이것이 위의 두 코드가 완전히 같은 이유다. 커스텀 Hook 을 추출하기 전과 후가 동일하다.

대신 여러 컴포넌트 간 state 자체를 공유할 필요가 있다면, state 를 위로 올려 전달하자.

 

2. Hook 사이에 상호작용하는 값 전달하기

커스텀 Hook 안의 코드는 컴포넌트가 리렌더링될 때마다 다시 돌아갈 것이다.

➡️ 커스텀 Hook 이 컴포넌트처럼 순수해야 하는 이유이다! 커스텀 Hook 을 컴포넌트 본체의 한 부분이라고 생각하자!

 

커스텀 Hook 이 컴포넌트와 함께 리렌더링된다면, 항상 가장 최신의 props 와 state 를 전달받을 것이다.

아래 채팅방 예시에서 서버 URL 이나 채팅방을 바꾼다고 생각해 보자.

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';
import { showNotification } from './notifications.js';

export default function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId
    };
    const connection = createConnection(options);
    connection.on('message', (msg) => {
      showNotification('New message: ' + msg);
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId, serverUrl]);

  return (
    <>
      <label>
        Server URL:
        <input value={serverUrl} onChange={e => setServerUrl(e.target.value)} />
      </label>
      <h1>Welcome to the {roomId} room!</h1>
    </>
  );
}

 

serverUrl 이나 roomId 를 변경할 때, effect 는 변화에 '반응'하며 재동기화한다. effect 의 의존성이 변경될 때마다 채팅방을 재연결하는 콘솔 메시지를 보낼 수 있다. 이제 Effect 코드를 커스텀 Hook 안에 넣어보자.

export function useChatRoom({ serverUrl, roomId }) {
  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId
    };
    const connection = createConnection(options);
    connection.connect();
    connection.on('message', (msg) => {
      showNotification('New message: ' + msg);
    });
    return () => connection.disconnect();
  }, [roomId, serverUrl]);
}

 

ChatRoom 컴포넌트가 내부 동작이 어떻게 동작하는지 걱정할 필요 없이 커스텀 Hook 을 호출할 수 있게 해준다.

export default function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useChatRoom({
    roomId: roomId,
    serverUrl: serverUrl
  });

  return (
    <>
      <label>
        Server URL:
        <input value={serverUrl} onChange={e => setServerUrl(e.target.value)} />
      </label>
      <h1>Welcome to the {roomId} room!</h1>
    </>
  );
}

 

매우 간단해졌다! (그런데도 똑같이 동작한다!)

매번 ChatRoom 이 리렌더링될 때마다, Hook 에 최신 roomId 와 serverUrl 값을 넘겨준다.

➡️ 리렌더링 이후 값이 달라지는지 여부에 관계없이 effect 가 재연결하는 이유이다.
만약 오디오 또는 비디오 처리 소프트웨어를 작업해 본 적이 있다면, 이처럼 Hook 을 연결하는 것이 시각적 혹은 청각적 효과를 연결하는 것을 떠오르게 할 것이다. 이게 바로 useState 의 결과를 useChatRoom 의 입력으로 '넣어주는 것' 과 같다.

 

3. 언제 커스텀 Hook 을 사용해야 하는가?

모든 자잘한 중복 코드들을 커스텀 Hook 으로 분리할 필요는 없다. 예시로, 하나의 useState 를 감싸기 위한 useFormInput 을 분리하는 것은 불필요하다.

 

하지만 effect 를 사용하든 사용하지 않든, 커스텀 Hook 안에 그것을 감싸는 게 좋은지 아닌지를 고려하자. effect 를 자주 쓸 필요가 없을지 모른다. 만약 effect 를 사용한다면, 그건 외부 시스템과 동기화한다던가 React 가 내장하지 않은 API 를 위해 무언가를 하는 등 'React 에서 벗어나기' 위함일 것이다. 커스텀 Hook 으로 감싸는 것은 목적을 정확하게 전달하고 어떻게 데이터가 그것을 통해 흐르는지 알 수 있게 해준다. 예시로 두 가지 목록을 보여주는 ShippingForm 컴포넌트를 살펴보자. 하나는 도시의 목록을 보여주고, 다른 하나는 선택된 도시의 구역 목록을 보여준다.

function ShippingForm({ country }) {
  const [cities, setCities] = useState(null);
  // 이 Effect는 나라별 도시를 불러옵니다.
  useEffect(() => {
    let ignore = false;
    fetch(`/api/cities?country=${country}`)
      .then(response => response.json())
      .then(json => {
        if (!ignore) {
          setCities(json);
        }
      });
    return () => {
      ignore = true;
    };
  }, [country]);

  const [city, setCity] = useState(null);
  const [areas, setAreas] = useState(null);
  // 이 Effect 선택된 도시의 구역을 불러옵니다.
  useEffect(() => {
    if (city) {
      let ignore = false;
      fetch(`/api/areas?city=${city}`)
        .then(response => response.json())
        .then(json => {
          if (!ignore) {
            setAreas(json);
          }
        });
      return () => {
        ignore = true;
      };
    }
  }, [city]);

  // ...

 

이 코드들은 반복적이지만, effect 들을 따로 분리하는 것이 옳다. 이들은 다른 두 가지(도시, 구역)를 동기화하므로 하나의 effect 로 통합시킬 필요가 없다. 대신 ShippingForm 컴포넌트를 useData 라는 커스텀 Hook 을 통해 공통된 로직을 추출할 수 있다.

function useData(url) {
  const [data, setData] = useState(null);
  useEffect(() => {
    if (url) {
      let ignore = false;
      fetch(url)
        .then(response => response.json())
        .then(json => {
          if (!ignore) {
            setData(json);
          }
        });
      return () => {
        ignore = true;
      };
    }
  }, [url]);
  return data;
}

 

이제 ShippingForm 컴포넌트 내부의 effect 들을 useData 로 교체할 수 있다.

function ShippingForm({ country }) {
  const cities = useData(`/api/cities?country=${country}`);
  const [city, setCity] = useState(null);
  const areas = useData(city ? `/api/areas?city=${city}` : null);
  // ...

 

커스텀 Hook 을 추출하는 것은 데이터의 흐름을 명확하게 해준다. url 을 입력하고 data 를 받는다.

useData 안의 effect 를 '숨김으로써' 다른 사람이 ShippingForm 컴포넌트에 불필요한 의존성을 추가하는 것을 막을 수 있다.

시간이 지나면 앱의 대부분 effect 들은 커스텀 Hook 안에 있을 것이다.

 

❗️ 커스텀 Hook 이 구체적인 고급 사용 사례에 집중하도록 하기 🔗

커스텀 Hook 의 이름은 누가 보아도 Hook 이 무슨 일을 하고, 무엇을 props 로 받고, 무엇을 반환하는지 알 수 있도록 매우 명확해야 한다. 또한 외부 시스템과 동기화할 땐, 이름에 좀 더 기술적이고 해당 시스템을 특정하는 용어를 사용하는 것이 좋다. 

  • ✅ useData(url)
  • ✅ useImpressionLog(eventName, extraData)
  • ✅ useChatRoom(options)
  • ✅ useMediaQuery(query)
  • ✅ useSocket(url)
  • ✅ useIntersectionObserver(ref, options)

커스텀 Hook 이 구체적인 고급 사용 사례에 집중할 수 있도록 하자. useEffect API 그 자체를 위한 대책이나 편리하게 감싸는 용도로 동작하는 커스텀 '생명 주기' Hook 을 생성하거나 사용하는 것을 피하자. 용도를 명확히 하자.

  • 🔴 useMount(fn)
  • 🔴 useEffectOnce(fn)
  • 🔴 useUpdateEffect(fn)

예시로, 위 useMount Hook 은 코드가 '마운트 시'에만 동작하는 것을 확인하기 위해 만들어졌다.

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  // 🔴 안 좋은 예 : 커스텀 "생명 주기" Hook을 사용
  useMount(() => {
    const connection = createConnection({ roomId, serverUrl });
    connection.connect();

    post('/analytics/event', { eventName: 'visit_chat' });
  });
  // ...
}

// 🔴 안 좋은 예 : 커스텀 "생명 주기" Hook을 생성
function useMount(fn) {
  useEffect(() => {
    fn();
  }, []); // 🔴 React Hook useEffect은 'fn'의 의존성을 갖고 있지 않음.
}

 

useMount 와 같은 커스텀 '생명 주기' Hook 은 전형적인 React 와 맞지 않는다. 이 코드 예시는 문제가 있지만(roomId 나 serverUrl 의 변화에 반응하지 않음), 린터는 오직 직접적인 useEffect 호출만 체크하기에 경고를 보내지 않는다. 린터는 Hook 에 대해 모르고 있다.

 

🤓 커스텀 Hook 은 더 나은 패턴으로 변경할 수 있도록 도와준다.

effect 는 탈출구이다. 'React 에서 벗어나'는 것이 필요할 때나 사용 시에 괜찮은 내장된 해결 방법이 없는 경우, 사용한다.

React 팀의 목표는 더 구체적인 문제에 더 구체적인 해결 방법을 제공해 앱에 있는 effect 의 숫자를 점차 최소한으로 줄이는 것이다.

커스텀 Hook 으로 effect 를 감싸는 것은 이런 해결 방법들이 가능해질 때 코드를 쉽게 업그레이드할 수 있게 해준다.

import { useState, useEffect } from 'react';

export function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    function handleOffline() {
      setIsOnline(false);
    }
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);
    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);
  return isOnline;
}

 

위 예시에서 useOnlineStatus 는 한 쌍의 useState 와 useEffect 와 함께 실행된다. 이것은 가장 좋은 해결 방법은 아니다.

이 방법이 고려하지 못한 어려 예외 상황이 존재하는데, 예시로 이건 컴포넌트가 마운트됐을 때 isOnline 이 이미 true 라고 가정한다.

하지만 이것은 네트워크가 이미 꺼졌을 때 틀린 가정이 된다. 이런 상황을 확인하기 위해 브라우저 navigator.onLine API 를 사용할 수도 있지만 이걸 직접적으로 사용하게 되면 초기 HTML 을 생성하기 위한 서버에선 동작하지 않는다. 그러므로 코드는 보완되어야 한다.

 

React 18 은 이런 모든 문제를 신경 써주는 useSyncExternalStore 라고 불리는 섬세한 API 를 포함한다.

아래는 새 API 의 장점을 가지고 다시 쓰인 useOnlineStatus 이다.

import { useSyncExternalStore } from 'react';

function subscribe(callback) {
  window.addEventListener('online', callback);
  window.addEventListener('offline', callback);
  return () => {
    window.removeEventListener('online', callback);
    window.removeEventListener('offline', callback);
  };
}

export function useOnlineStatus() {
  return useSyncExternalStore(
    subscribe,
    () => navigator.onLine, // 클라이언트의 값을 받아오는 방법
    () => true // 서버의 값을 받아오는 방법
  );
}

 

어떻게 이 변경을 하기 위해 다른 컴포넌트들을 변경하지 않아도 되는지 알아보자.

function StatusBar() {
  const isOnline = useOnlineStatus();
  // ...
}

function SaveButton() {
  const isOnline = useOnlineStatus();
  // ...
}

 

커스텀 Hook 으로 effect 를 감싸는 것이 종종 유용한 이유는 다음과 같다.

  1. 매우 명확하게 effect 로 주고받는 데이터 흐름을 만들 때
  2. 컴포넌트가 effect 의 정확한 실행보다 목적에 집중하도록 할 때
  3. React 가 새 기능을 추가할 때, 다른 컴포넌트의 변경 없이 이 effect 를 삭제할 수 있을 때

디자인 시스템과 마찬가지로, 앱의 컴포넌트에서 일반적인 관용구를 추출해 커스텀 Hook 으로 만드는 것이 도움이 될 수 있다.

그럼 컴포넌트의 코드가 의도에 집중할 수 있고, effect 를 자주 작성하지 않아도 된다.

 

  • 하나의 Hook에서 다른 Hook으로 반응형 값을 전달할 수 있고, 값은 최신 상태로 유지된다.
  • 커스텀 Hook 을 통해 받는 이벤트 핸들러는 effect 로 감싸야 한다. 🔗
  • 코드의 경계를 선택하는 방법과 위치는 우리가 결정할 수 있다.
  • 가끔 Hook 이 필요하지 않을 수도 있다.

 

 

 

🔗 https://ko.react.dev/learn/reusing-logic-with-custom-hooks

 

커스텀 Hook으로 로직 재사용하기 – React

The library for web and native user interfaces

ko.react.dev

 

 

 

728x90
반응형