[React] 리액트에서 제공하는 Hooks란? (feat. 커스텀 Hook 만들기)
리액트에서 Hooks란?
Hook(이하 훅)이란 함수형 컴포넌트에서 상태(state)와 생명주기(life cycle) 기능을 사용할 수 있게 해주는
리액트에서 제공하는 기능이다.
훅은 함수형 컴포넌트 내에서만 사용할 수 있으며, 훅의 등장으로 리액트의 생태환경에서
함수형 컴포넌트가 클래스 컴포넌트를 대신하게 되었다.
Hooks의 탄생배경
지금의 리액트 개발은 대부분이 함수형(Funtion) 컴포넌트를 이용해서 개발하고 있지만
기존의 리액트(16.8v 이전)는 지금과 다르게 클래스형(Class) 컴포넌트로 개발되어 왔다.
그 이유로는 state와 생명주기 API를 제공하는 클래스형 컴포넌트와는 다르게
기존 함수형 컴포넌트는 단순히 UI를 랜더링해 화면에 표출해주는 용도로만 사용할 수 있었다.
하지만 클래스형 컴포넌트는 render 함수를 직접 호출해야 하는 등 복잡했기 때문에
보다 간결하게 코드를 작성할 수 있는 함수형 컴포넌트를 지원하기 위해 Hooks(훅)이라는 새로운 기능이 나왔다.
훅이 등장하게 되면서 함수형 컴포넌트는 기존 클래스 컴포넌트의 모든 기능을 사용할 수 있게 되고
기존의 간결한 코드 또한 유지되면서 많은 개발자들이 사용하게 되었다.
Hook의 종류와 사용방법
훅의 종류
- useState: 상태(state)를 함수형 컴포넌트에서 관리할 수 있게 해주는 훅입니다.
useState를 사용하면 컴포넌트의 상태를 선언하고 변경할 수 있습니다. - useEffect: 생명주기(lifecycle) 함수의 역할을 수행하는 훅입니다. 컴포넌트가 렌더링될 때마다
특정 작업을 수행하거나, 상태가 변경될 때마다 특정 작업을 수행하는 등의 작업을 처리할 수 있습니다. - useContext: 리액트의 Context API를 사용하여 전역 상태를 관리할 때 사용되는 훅입니다.
useContext를 사용하면 함수형 컴포넌트에서 쉽게 전역 상태를 사용할 수 있습니다. - useRef: DOM 요소에 접근하거나, 이전 값에 대한 참조를 유지하거나,
외부 라이브러리에 대한 참조를 저장하는 등의 작업에 사용되는 훅입니다. - useReducer: 복잡한 상태 로직을 처리할 때 사용되는 훅으로, useState의 대안으로 활용됩니다.
상태 업데이트 로직을 리듀서 함수로 분리하여 상태를 업데이트할 수 있습니다. - useCallback: 함수를 메모이제이션하여 성능을 최적화할 때 사용되는 훅입니다.
특정 함수를 새로 생성하지 않고도 이전에 생성한 함수를 재사용할 수 있도록 해줍니다. - useMemo: 값을 메모이제이션하여 성능을 최적화할 때 사용되는 훅입니다.
특정 값을 새로 계산하지 않고도 이전에 계산한 값을 재사용할 수 있도록 해줍니다. - useLayoutEffect: useEffect와 비슷하지만, 브라우저에 업데이트를 반영하기 전에
동기적으로 작업을 수행하는 훅입니다.
훅의 특징
1. 함수 컴포넌트 또는 커스텀 훅 내부에서만 호출 가능
2. 조건부 또는 반복문 내에서는 호출 될 수 없다.
3. 나만의 훅 (Custom Hook)을 만들어 사용할 수 있다.
어떤 경우에 커스텀 Hook을 만들어 사용할까?
아래와 같은 코드가 있다고 가정해보자.
MyHook1
import { React, useState } from "react";
export const MyHook1 = (props) => {
const [string, setString] = useState("");
const onChangeInput = (e) => {
setString(e.target.value);
};
return (
<>
<div>{string}</div>
<input onChange={onChangeInput} />
</>
);
};
MyHook2
import { React, useState } from "react";
export const MyHook2 = (props) => {
const [string, setString] = useState("");
const onChangeInput = (e) => {
setString(e.target.value);
};
return (
<>
<div>{string}</div>
<input onChange={onChangeInput} />
</>
);
};
[myHook1, myHook2] 이렇게 2개의 컴포넌트가 있는데,
가만히 보면 로직이 똑같다. ( 물론 이건 샘플이다.. 아예 똑같은 로직이면 보통 컴포넌트 1개로 만들겠지만.. )
중복되는 코드들이 로직이 보인다.
<inpuit>의 value값이 변경 될 때, onChange 이벤트 핸들러가 동작하고 onChangeInput이라는 함수를 호출한다.
그렇게 string이라는 state값을 변경하는데... 이렇게 자주 쓰이는 로직들은
따로 훅으로 만들어 각 컴포넌트에서 사용하면 중복되는 코드들을 줄일 수 있다.
javascript에서 자주 쓰이는 기능들을 모듈로 빼서 사용하는 것과 비슷하다고 생각하면 된다.
커스텀 Hook 만들기
커스텀hook은 component디렉터리가 아닌 hooks라는 디렉토리를 만들어 해당 디렉토리에서
관리하는게 일반적임
커스텀 훅을 만드는 건 간단하다.
1. Hook(훅)을 만들 때, 이름 앞에 use를 붙이면 리액트에서는 훅으로 인식한다.
2. 자주 사용되는 로직을 훅에 넣고 [값, 함수] 형태로 리턴해주면 된다.
아래는 샘플로 만들어 본 훅이다..
useChange.jsx
import { useState } from "react";
export const useChnage = () => {
// useState로 state를 선언
const [string, setString] = useState("");
// 자주 사용되는 함수에서 로직 수행
const onChangeInput = (e) => {
setString(e.target.value);
};
// state값과 해당 함수를 담은 배열을 리턴
return [string, onChangeInput];
};
SampleHook.jsx
// 생성한 Hook(훅)을 import함
import { useChnage } from "../hooks/useChange";
export const MyHook1 = (props) => {
// 일반 Hook(훅)을 생성 하듯이 state를 생성..
// 단 배열안의 값은 커스텀 훅이 리턴하는 배열과 동일해야함
const [string, onChangeInput] = useChnage();
return (
<>
<div>{string}</div>
<input onChange={onChangeInput} />
</>
);
};
export const MyHook2 = (props) => {
const [string, onChangeInput] = useChnage();
return (
<>
<div>{string}</div>
<input onChange={onChangeInput} />
</>
);
};
이렇게 커스텀훅을 사용하면 기존의 로직과 똑같이 동작하는 것을 확인할 수 있다.
결과
추가적으로 이렇게 작성된 커스텀 훅은 일반적으로 src디렉터리 안에
hooks이라는 디렉터리를 생성하고 해당 디렉터리에서 관리한다고 한다..