Custom hook 기초
시작하며
React를 통해 개발하면서 컴포넌트마다 똑같은 코드가 반복되곤 하는데 그런 코드가 심지어 길이도 작지 않다면 컴포넌트가 길어져서 곤란하다. 이런 문제를 custom hook을 통해 해결할 수 있는 부분이 있을 것이라 생각이 들어 새로 공부하게 되었다.
Custom hook의 장점
- 클래스 컴포넌트보다 적은 코드로 동일한 기능을 구현할 수 있다.
- 코드 길이를 줄이면서 가독성은 높일 수 있다.
Custom hook 규칙
- 최상위에서 hook을 호출한다 (반복문이나 조건문에서 hook을 호출하지 않는다.)
- React 함수 내에서만 hook을 호출한다.
- hook의 이름을 'use'로 시작한다.
Hook 만들 때 고려해야 하는 점
- custom hook을 만들 때는 다른 hook의 state에 영향을 끼치게 해서는 안된다. 즉, 각각의 state가 고유성을 가지고 있어야 하는데 고유성을 지키지 않는 경우 예기치 못한 문제나 이슈가 발생했을 때 그 원인을 파악하기도 어렵다.
- custom hook이 에러나 코드를 보는데 있어서 인과관계를 잘 파악할 수 있게 해야 한다.
기존에 쓰던 반복되는 로직을 가능하면 custom hook으로 깔끔하게 만들려고 했는데 그랬다가는 가독성이나 오히려 hook을 수정하는데 더 시간이 걸리고 에러도 발생하겠다는 생각이 들었다. 그러면 어떤 상황에서 hook을 사용하는게 좋을지 궁금해서 좋은 custom hook의 예리를 추가로 정리해보았다.
좋은 custom hook 예시
1. Enter / Esc key hook
특정 버튼이 클릭하고 클릭하지 않는 것에 대해 핸들링하는 부분의 로직이 길어졌는데 아래 hook을 통해 로직을 줄일 수 있고 또 다른 상태와 state 역시 겹치지 않는 다는 점에서 고유성을 지킨 예시인 것 같다.
import { useEffect } from 'react';
interface IUseEnterEscButtonsProps {
handleCancel: Function,
handleConfirm: Function,
}
export const useEnterEscButtons = ({handleCancel, handleConfirm}:IUseEnterEscButtonsProps)=>{
useEffect(()=>{
const listener = (event: {code:string, preventDefault: ()=>void}) => {
if((event.code === 'Enter' || event.code === 'NumpadEnter')){
handleConfirm();
event.preventDefault();
}
}
document.addEventListener('keydown', listener);
return () => {
document.removeEventListener('keydown', listener);
}
},[handleConfirm]);
useEffect(()=>{
const listener = (event:{code:string, preventDefault:() => void}) => {
if(event.code === 'Escape'){
handleCancel();
event.preventDefault();
}
}
document.addEventListener('keydown', listener);
return () => {
document.removeEventListener('keydown', listener);
}
},[handleCancel]);
}
2. debounce hook
debounce는 여러번의 요청이 발생하는 경우 모든 요청을 그대로 처리하는 것이 아니라 요청의 마지막에서 특정 시간이 지난 후 하나의 요청만 보내는 것으로 resizing이나 검색어 추천등에 사용된다. 아래 hook은 input의 onChange 등의 이벤트가 있는 경우 어느정도 요청이 을 보낸 후 마지막 요청을 보냈을 때 delay 타임 이후 요청한 값에 대한 처리를 시작한다.
interface IUSerDebounce {
value:any,
delay:number
}
const UseDebounce = ({value, delay}:IUSerDebounce) => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(()=>{
const handler = setTimeout(()=>{
setDebouncedValue(value)
}, delay)
return () => {
clearTimeout(handler);
}
}, [value, delay])
return debouncedValue;
}
3. resizing hook
반응형을 width에 따라 구현해야 하는데 라이브러리를 사용하는 것도 좋지만 의존성을 줄이기 위해 아래와 같이 간단하게 custom hook을 작성해서 사용할 수 있다.
import React, { useEffect, useState } from 'react';
const useWindowResize = () => {
const [width, setWidth] = useState(window.innerWidth);
const [height, setHeight] = useState(window.innerHeight);
const listener = () => {
setWidth(window.innerWidth);
setHeight(window.innerHeight);
}
useEffect(()=>{
window.addEventListener(
"resize", listener
)
return () => {
window.removeEventListener("resize", listener)
};
},[])
return {
width,
height
}
}
export default useWindowResize;
혼자 개발을 한다면 모르겠지만 협업을 하는 과정에서 custom hook은 그 사용에 의한 결과를 잘 생각해보고 사용해야겠다는 생각을 하게 된다. 단순히 깔끔해보이려고 사용했다기 보다는 정확히 custom hook을 사용할 수 있는 지점들이 있을 것이라 보인다. 자주 사용해 보면서 최적으로 사용할 수 있는 지점에 대한 감을 익혀봐야겠다.
아래의 내용을 참고했습니다.
https://betterprogramming.pub/react-custom-hooks-with-real-life-examples-c259139c3d71