커스텀 훅으로 컴포넌트 로직을 함수로 뽑아내어 재사용할 수 있다. - 공식 문서
예를 들어 상태 변수중에 컴포넌트가 마운트되고 data를 fetch해서 useEffect에서 상태값을 변경해줘야 하는 경우에 사용할 수 있다.
적용
현재 진행하고 있는 프로젝트에서, 많은 페이지에서 검색 필터 팝업을 공통적으로 활용하고 있다. 현재 검색 필터 팝업은 외부를 클릭했을 때 팝업창이 닫히지 않는 문제가 있어서, PM님이 이 부분을 해결해달라고 했다.
기존의 로직은 컴포넌트에서 열림 상태를 관리한다. isOpen && <FilterModal/> , <FilterButton onClick={setIsOpenFilter(true)}/> 이런 식으로 버튼 눌렀을때 isOpen을 true로 만들고 팝업이 뜨게 하는 로직이다.
이 팝업창의 외부를 클릭시 닫히게 하기 위해서 2가지 방법을 고려했다.
1) 외부에 투명배경의 viewport 전체를 덮는 dom을 하나 만들어서 onClick={setIsOpenFilter(false)} 해준다.
2) 팝업 생성시 useEffect로 window에 클릭 이벤트를 추가한다. 클릭 시에 타겟이 팝업창 안에 있으면 그대로 두고 아니면 setIsOpenFilter(false) 해준다.
1)은 이전에 구현해본 방식이어서 2) 방식을 택했다.
핵심 로직은 다음과 같다.
1. 필터 팝업이 isOpen이 true가 되면서 렌더링이 다시 되서 마운트 된다.
2. useEffect로 마운트될때 window에 클릭 이벤트를 추가해준다. 2)에 설명한 내용
3. 마운트 해제될때 useEffect의 return으로 클릭 이벤트를 없애준다.
필터 팝업이 굉장히 많은 페이지에 들어가기 때문에 들어가는 컴포넌트들마다 해당 로직을 추가하는 것은 중복이 너무 많이 늘어나게 된다.
그래서 커스텀훅으로 따로 빼서 중복을 줄이고 쉽게 사용할 수 있게 했다. 코드는 다음과 같다.
import { useEffect, useState } from "react"
const conditionTypes = ["menu-type", "menu-dataType", "menu-scopeType"]
const useSearchFilterStatus = (filterRef, initialState) => {
const [searchFilter, setSearchFilter] = useState(initialState)
useEffect(() => {
const onClick = (e) => {
const target = e.target
if(target.tagName === 'BODY') {
// 텍스트필드 클릭 시 타겟이 body로 잡히기 때문에 추가
return
}
if(e.path.findIndex(e => conditionTypes.includes(e.id)) !== -1) {
// 셀렉트에 들어간 메뉴아이템들 인지 확인
return
}
if(filterRef.current !== null && !filterRef.current.contains(target)) {
setSearchFilter(false)
}
}
if(searchFilter) {
window.addEventListener('click', onClick)
}
return () => {
if(searchFilter) {
window.removeEventListener('click', onClick)
}
}
},[searchFilter])
return [searchFilter, setSearchFilter]
}
export default useSearchFilterStatus
이게 material UI의 경우에는 select창 클릭시 target이 body로 잡히거나 이상하게 잡힌다. 그래서 부득이하게 if 문을 추가해서 예외처리를 해줘야 했다. 나머지는 위에서 설명한 로직과 같다.
이 커스텀 훅을 각 컴포넌트에서 import해서 사용하게 했다.
커스텀 훅은 앞으로도 매우 유용하게 사용할 수 있을 것 같다. data fetch해서 세팅하는 부분도 커스텀훅으로 리팩토링하면 코드가 간결해지고 중복이 줄어들 것 같다.
'프론트엔드 > React' 카테고리의 다른 글
useIntersectionObserverRef 커스텀 훅 만들기 (1) | 2023.02.16 |
---|---|
Portal (0) | 2022.07.06 |
HOC (1) | 2022.06.04 |
useCallback 과 useMemo (0) | 2022.06.04 |
Memoization (0) | 2022.06.04 |