공부기록/[강의노트] Udemy React 완벽가이드 101~200

# 195 [udemy React 완벽 가이드 노트] Custom Hooks 만들기

Jenner 2022. 10. 4. 18:29

Custom Hooks란?


 

정규 함수인데 stateful logic이다. (안에 state를 설정할 수 있는 로직을 포함한 함수) 

 

커스텀 훅을 만들면 재사용 가능한 함수에 

state를 설정하는 로직을 아웃소싱할 수 있다. 

 

커스텀 훅은 다른 커스텀 훅을 포함한 리액트 훅을 사용할 수 있다. 

 

커스텀 훅을 통해 다른 컴포넌트에서 사용할 수 있는 로직을 

커스텀 훅으로 아웃소싱 할 수 있으며 이를 통해 다양한 컴포넌트에서 호출 가능하다. 

 

즉, 로직 재사용이 가능한 매커니즘이다. 

커스텀 훅에서는 리액트 훅과 다른 훅도 사용가능하다. 

 

 

 

Custom Hooks 필요한 이유


상호간에 연관된 작업을 수행하는 

서로다른 컴포넌트들이 있는경우 

 

지금의 경우에는 BackwardCounter와 ForwardCounter간에 비슷한 부분이 많다. 

 

 

import { useState, useEffect } from "react";

import Card from "./Card";

const BackwardCounter = () => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter(prevCounter => prevCounter - 1);
    }, 1000);

    return () => clearInterval(interval);
  }, []); 

  return <Card>{counter}</Card>;
};

export default BackwardCounter;

 

 

 

import { useState, useEffect } from 'react';

import Card from './Card';

const ForwardCounter = () => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter((prevCounter) => prevCounter + 1);
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return <Card>{counter}</Card>;
};

export default ForwardCounter;

 

 

그런데 우리가 재사용 하려는 코드가 

useState나 useEffect 같은 리액트 훅을 사용하는 것이 아니고 

state 업데이트 함수를 호출하여 상태를 갱신하게 된다. 

 

이 때 다른 함수에서 리액트 훅을 사용하는 것은 불가능하다.(규칙에 어긋난다)

리액트 컴포넌트 함수나 커스텀 훅에서만 사용할 수 있다. 

 

별도의 함수에 이러한 공통 로직을 아웃소싱하려면 커스텀 훅을 만들어야 한다. 

 

 

 

커스텀 훅 만들기 


파일을 만든다. 이름은 아무렇게나 해도 되지만 

컴포넌트명은 반드시 use로 시작되어야 한다. 

그래야 리액트에게 이 함수가 커스텀 훅임을 알려주며 

리액트가 해당 함수를 훅의 규칙에 따라 사용할 것을 보장해 준다.

이 커스텀 훅을 내장 훅과 같은 방식으로 쓸 수 있게 되는 것이다. 

 use로 시작해야 훅의 규칙을 위반하여 사용했을 때 리액트가 경고를 보낼 수 있게 된다. 

 

 

 

만들어야 하는 로직을 그대로 복사해 넣어준다. 

import { useState, useEffect } from "react";

const useCounter = () => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter(prevCounter => prevCounter + 1);
    }, 1000);

    return () => clearInterval(interval);
  }, []);
};

export default useCounter;

 

사용방법은 내장 훅을 사용하는 방법과 동일하다. 

함수와 같이 호출해 주면 된다. 

 

 

 

 

사용하고자 하는 컴포넌트에 import를 해주고 호출해 준다. 

 

 

import { useState, useEffect } from "react";

import Card from "./Card";
import useCounter from "../hooks/use-counter";

const ForwardCounter = () => {
  useCounter();

 

 

컴포넌트가 어떤 상태나 효과를 등록한다면,

그 상태나 효과가 커스텀 훅을 사용하고 있는 컴포넌트에 묶이게 된다. 

그래서 ForwardCounter에서 useCounter를 호출하면 

useCounter에서 만들어진 state가 ForwardCounter에 묶이는 것이다. 

 

내장 훅과 같이 여러 컴포넌트에서 커스텀 훅을 사용하면 

각각의 컴포넌트들이 각각의 state를 받게 된다. 

 

ForwardCounter에서 useCounter를 호출했으므로 

useCounter의 counter 상태는 ForwardCounter에 대해 설정된다. 

그리고 useEffect역시 ForwardCounter에서 설정되거나 발동된다. 

 

 

 

커스텀 훅이 관리하는 counter state를 사용해보자

내장 훅은 백그라운드에서 상태를 만들거나 상태를 관리 한다. 

그리고 상태에 대한 배열을 반환한다. 

 

useCounter에서는 커스텀 훅을 사용하는 컴포넌트에서  counter의 상태를 사용가능하게 하려면 

return 하면 된다. 

 

 

 

const useCounter = () => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter(prevCounter => prevCounter + 1);
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return counter;
};

 

 

 

커스텀 훅에서는 필요한 무엇이든 반환할 수 있다. 

배열, 객체, 숫자 등...

 

커스텀 훅을 사용하는 컴포넌트에서는 

반환되는 값을 이용할 수 있게 된다. 

const 변수로 저장하면 값을 반환하게 된다. 

 

 

이제 ForwardCounter에 나머지 코드들을 삭제해도 

이전처럼 정상 작동한다.

import Card from "./Card";
import useCounter from "../hooks/use-counter";

const ForwardCounter = () => {
  const counter = useCounter();

  return <Card>{counter}</Card>;
};

export default ForwardCounter;

 

 

 

BackwardCounter에서도 사용해보자. 

 

BackwardCounter 컴포넌트는 + 대신 -를 사용하고 있는 것을 제외하면 

ForwardCounter과 모두 같은 로직을 사용하고 있다. 

 

커스텀 훅도 매개변수를 받아들일 수 있다. 

내장 훅 중 useState는 인자를 받아 초기상태를 설정할 수 있고 

useEffect는 첫 번째 인자로 함수를 받고 두 번째 인자로 dependencies를 받는다

그렇듯이 커스텀 훅도 매개변수를 받을 수 있는 것이다. 

 

 

커스텀 훅에 매개변수를 넣어주자

const useCounter = counterUpdateFn => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter(counterUpdateFn());
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return counter;
};

이렇게 변경해 주면 작동을 상황에 따라 변경할 수 있게 된다. 

 

 

혹은 불리언 플래그와 같은 것을 사용할 수도 있다. 

 

 

const useCounter = (forwards = true) => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      if (forwards) {
        setCounter(prevCounter => prevCounter + 1);
      } else {
        setCounter(prevCounter => prevCounter - 1);
      }
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return counter;
};

 

 

dependencies를 변경해주자. 

useCounter안에서 forwards는 

useEffect 함수 내에서 정의된 것이 아니고 

커스텀 훅 외부에서 설정된 것도 아니다. 

매개변수로 받게 되는 값이다. 

그래서 dependencies로 추가해야 하는 것이다.  

 

사용하는 컴포넌트에서 항상 forwards가 true이거나 false인 경우가 있으나 

컴포넌트마다 forwards의 불리언값이 달라질 수 있다 

그럴 때마다 함수를 재 실행해야 하므로 

dependencies에 forwards를 추가하는 것이 옳다. 

 

 

이제 BackwardCounter에서도 커스텀훅을 사용할 수 있게 되었다. 

커스텀훅을 불러와 counter에 저장해주고,

인자로 false를 전달해주면 된다. 

(forwardCounter에서는 인자를 전달해주지 않으면

default값으로 true가 되므로 forwards = true가 되는 것이다.)

 

 

그러나 여태까지는 그냥 현실적이기 보다는

소개하는 정도의 커스텀 훅이었다. 

다음 강의에서 

보다 현실적인 커스텀 훅을 살펴보자.