Home Hook
Post
X

Hook

Hooks

Hook이 나오기 전까지는 State에 접근하거나 생명주기 기능(lifecycle features)을 사용하기 위해서는 클래스 컴포넌트를 선언을 해줘야 했습니다.

하지만 클래스 컴포넌트는 로직의 재사용이 불가능하고 코드가 복잡하다는 단점이 있습니다.

React의 Hook은 16.8 버전부터 추가된 기능으로 함수 컴포넌트에서 React State와 생명주기 기능(lifecycle features)을 **'연동(hook into)'**할 수 있게 해주는 함수입니다.

즉, 클래스형 컴포넌트를 사용하지 않고도 함수형 컴포넌트에서 상태값 접근과 자식 요소에 접근이 가능하도록 하는 것이 Hook입니다.

Hooks


Hook 규칙

  • 최상위 레벨에서만 Hook을 호출해야합니다.

    반복문, 조건문, 중첩된 함수 내에서 Hook 사용 X

  • React 함수 컴포넌트 내에서만 Hook을 호출해야합니다.


useState

React의 Hook 중 하나로 컴포넌트의 State를 간편하게 생성하고 업데이트 할 수 있게 해주는 도구를 제공합니다.

const [변수명, set함수명] = useState(초기값); 의 형태로 사용합니다.

1
2
3
4
5
6
7
8
9
10
const Counter = (props) => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>{count}번 클릭했습니다. </p>
      <button onClick={() => setCount(count + 1)}>클릭</button>
    </div>
  );
};

setCount 함수를 통해 count 값이 변경되면 컴포넌트가 리렌더링되면서, 화면에 새로운 값이 표시됩니다.

useEffect

컴포넌트가 렌더링될 때 특정 작업을 실행할 수 있도록 하는 Hook입니다.

React의 함수 컴포넌트에서 Side Effect를 실행할 수 있게 해주는 Hook으로 클래스 컴포넌트에서 제공하는 생명주기 함수 componentDidMount, componentDidUpdate, componentWillUnmount의 기능을 모두 수행할 수 있습니다.


useEffect 사용법

  • useEffect(이펙트 함수)

    모든 컴포넌트가 리렌더링될 때마다 실행됩니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    const Counter = (props) => {
      const [count, setCount] = useState(0);
    
      useEffect(() => {
        document.title = `You clicked ${count} times`;
      });
    
      return (
        <div>
          <p>{count} 번 클릭했습니다. </p>
          <button onClick={() => setCount(count + 1)}>클릭</button>
        </div>
      );
    };
    

    의존성 배열 없이 useEffect를 사용하면, 컴포넌트가 처음 렌더링될 때를 포함해서, DOM이 렌더링 될 때마다(update 될 때마다) Effect 함수가 실행됩니다.

    즉, componentDidMount & componentDidUpdate 와 비슷하게 작동합니다.

  • useEffect(이펙트 함수, 의존성 배열)

    의존성 배열이란 이펙트 함수가 의존하는 배열입니다.

    배열 안의 변수 중 하나라도 값이 변경되면 이펙트 함수가 실행됩니다.

    특정한 값이 변경될 때 effect함수를 실행 하고 싶을 경우 배열 안에 그 값을 넣어줍니다.

    이 때, 빈 배열을 입력하면 컴포넌트가 Mount 될 때에만 실행됩니다.

    즉, componentDidMount와 비슷하게 작동합니다.

  • cleanup 함수

    useEffect 안에서 return 하는 함수로 컴포넌트가 Unmount되기 직전 실행됩니다.

    즉, componentWillUnmount와 비슷하게 작동합니다.


useEffect 최종 정리

1
2
3
4
5
6
7
8
9
10
11
12
useEffect(() => {
  // 컴포넌트 Mount 시에는 항상 실행
  // 의존성 배열에 있는 변수가 변경시 실행
  // 의존성 배열이 빈 배열이면 업데이트 실행 X
  // 의존성 배열 생략 시 모든 업데이트 마다 실행

  // cleanup 함수
  return () => {
    // 컴포넌트가 Unmount 되기 직전 실행
  }
}, [의존성 변수1, 의존성 변수2, ...]) // 빈 배열 & 생략 가능


useMemo

useMemo는 React에서 컴포넌트의 성능을 최적화하는데 사용하는 Hook입니다.

memo는 memoization을 뜻하는데 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술을 말합니다.

React에서 함수 컴포넌트는 렌더링 => 컴포넌트 함수 호출 => 모든 내부 변수 초기화의 순서로 작동합니다.

1
2
3
4
5
6
7
8
9
function Component() {
  const value = calculate();

  return <div>{value}</div>;
}

function calculate() {
  return 10;
}

해당 예시에서 컴포넌트가 렌더링 될 때마다 value 변수는 초기화됩니다.

즉, calculate 함수가 재호출 되는 것으로 비효율적입니다.

이 때, useMemo를 사용하면 렌더링 => 컴포넌트 함수 호출 => Memoize된 함수를 재사용의 순서로 작동합니다.

즉, calculate 함수를 반복적으로 실행할 필요가 없습니다.


useMemo 사용법

1
2
3
const value = useMemo(() => {
  return calculate();
}, [item]);

useEffect와 똑같이 첫 번째 인자로 콜백 함수, 두 번째 인자로 의존성 배열을 받습니다.

의존성 배열안의 변수가 변경되면 콜백 함수를 다시 호출하여 메모리에 저장된 값을 업데이트합니다.


useCallback

useMemo와 유사하지만 값이 아닌 함수를 반환합니다.

즉, useMemo는 값을 memoization하는 반면, useCallback은 콜백 함수 자체를 memoization하는 것입니다.


useCallback 사용법

1
2
3
4
5
6
const calculate = useCallback(
  (num) => {
    return num + 1;
  },
  [item]
);

역시 첫 번째 인자로 콜백 함수를 두 번째 인자로 의존성 배열을 받습니다.


useRef

저장공간 또는 DOM 요소에 접근하기 위해 사용되는 Hook입니다.

useRef는 값을 변경해도 컴포넌트가 재런더링 되지 않습니다.

또한, 컴포넌트가 재렌더링 되어도 Ref에 저장된 값은 초기화되지 않습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const App = () => {
  const [stateCount, setStateCount] = useState(0);
  const refCount = useRef(0);

  function upState() {
    setStateCount(stateCount + 1);
  }

  function upRef() {
    ++refCount.current;
  }

  return (
    <div>
      <div>stateCount : {stateCount} </div>
      <div>refCount : {refCount.current} </div>
      <br />
      <button onClick={upState}>State up</button>
      <button onClick={upRef}>Ref up</button>
    </div>
  );
};

Ref up 버튼을 누르면 refCount 값은 올라가지만 화면이 랜더링 되지 않습니다.
State up 버튼을 누르면 화면이 랜더링 되어 그동안 변경되었던 refCount 값이 출력되는 것을 볼 수 있습니다.

useRef는 .current 프로퍼티로 전달된 인자로 초기화된 변경 가능한 ref객체를 반환합니다.

React를 사용하는 프로젝트에서도 DOM 을 직접 선택해야 하는 상황이 필요할 때 우리는 useRef라는 React Hook을 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const App = () => {
  const inputRef = useRef();

  function focus() {
    inputRef.current.focus();
    console.log(inputRef.current);
  }

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="아이디" />
      <button>Login</button>
      <br />
      <button onClick={focus}>focus</button>
    </div>
  );
};

Custom Hook

React에서 구성 요소 전체에서 기능을 캡슐화하여 재사용 가능하도록 하는 방법으로 Custom Hook을 사용합니다.

Custom Hook은 복잡한 논리 또는 상태 관리를 단일 기능으로 캡슐화하여 컴포넌트를 더 단순하고 집중적으로 만들 수 있다는 장점이 있습니다.

Custom Hook은 이름이 use로 시작하고 내부에서 다른 Hook을 호출하는 하나의 자바스크립트 함수입니다.

따라서, Custom Hook을 사용할 때는 함수를 요소로 가져오고 다른 함수처럼 호출하기만 하면 됩니다.

  • boolean 값을 관리하는 Custom Hook 예시

    선택적 initialValue 매개변수를 받고 현재 값과 값을 업데이트하는 toggle 함수를 배열로 반환하는 useToggle이라는 Custom Hook입니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    // hooks.jsx
    import { useState } from "react";
    
    const useToggle = (initialValue = false) => {
      const [value, setValue] = useState(initialValue);
    
      const toggle = () => {
        setValue(!value);
      };
    
      return [value, toggle];
    };
    

    사용할 때는 간단하게 다른 함수처럼 호출하면 됩니다.

만약 Custom Hook을 다른 파일로 분리한 경우 export로 내보낸 커스텀 훅을 import해서 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
import { useToggle } from "./hooks";

const MyComponent = () => {
  const [value, toggle] = useToggle();

  return (
    <div>
      <p>Value: {value.toString()}</p>
      <button onClick={toggle}>Toggle</button>
    </div>
  );
};

Custom Hook 만들기

url을 인자로 받아 fetch 메서드를 통해 가져온 데이터와 로딩 플래그를 배열로 반환하는 useFetch 커스텀 훅을 만들어보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import { useState, useEffect } from "react";

const useFetch = (url) => {
  // 데이터를 담을 State 초기화
  const [data, setData] = useState(null);
  // 로딩 중인지를 나타내는 로딩 플래그 초기화
  const [loading, setLoading] = useState(true);

  // fetch 메서드로 인자로 받은 url에서 데이터를 받아옵니다.
  const fetchData = async () => {
    const response = await fetch(url);
    const json = await response.json();

    // 받아온 데이터로 State 변경
    setData(json);

    // 로딩 플래그 false
    setLoading(false);
  };

  useEffect(() => {
    fetchData();
  }, [url]);

  // 배열 형태로 반환합니다.
  return [data, loading];
};

const MyComponent = () => {
  const [data, loading] = useFetch(
    "https://jsonplaceholder.typicode.com/users"
  );

  if (loading) {
    return <div>Loading...</div>;
  }

  return (
    <ul>
      {data.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
};
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.