import { useState } from "react"
const Button = () => {
const [isOpen, setOpen] = useState<boolean>(false)
const handleOpen = () => {
setOpen(!isOpen)
}
return (
<button type="button" onClick={handleOpen}>
{isOpen ? "열렸다" : "닫혔다"}
</button>
)
}
export default Button
외부에서 클릭하는 걸 감지 하고 싶다면?
useRef 사용하기!
import { useEffect, useRef, useState } from "react"
const Button = () => {
const [isOpen, setOpen] = useState<boolean>(false)
const ref = useRef<HTMLButtonElement>(null)
const handleOpen = () => {
setOpen(!isOpen)
}
// 외부 이벤트 감지용
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
setOpen(false)
}
}
document.addEventListener("mousedown", handleClickOutside)
return () => {
document.removeEventListener("mousedown", handleClickOutside)
}
}, [])
return (
<button type="button" onClick={handleOpen} ref={ref}>
{isOpen ? "열렸다" : "닫혔다"}
</button>
)
}
export default Button
좀 더 생각해볼 점
`useEffect` isOpen Depandency
방법 1.
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
setOpen(false)
}
}
document.addEventListener("mousedown", handleClickOutside)
return () => {
document.removeEventListener("mousedown", handleClickOutside)
}
}, [])
방법 2.
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (isOpen && ref.current && !ref.current.contains(event.target as Node)) {
setOpen(false)
}
}
document.addEventListener("mousedown", handleClickOutside)
return () => {
document.removeEventListener("mousedown", handleClickOutside)
}
}, [isOpen])
방법 1의 문제점.
외부 클릭시 매번 아래의 함수 실행됨.
-> 불필요한 호출 발생?
if (ref.current && !ref.current.contains(event.target as Node)) {
setOpen(false)
}
방법 2의 문제점.
버튼이 클릭 될 때 마다 (isOpen이 변경될때마다), 이벤트가 등록/삭제가 발생합니다.
결론
사실 브라우저가 이러한 연산을 효율적으로 처리할 수 있기 때문에 대부분 문제는 되지 않습니다.
`isOpen` 상태가 자주 변경되지 않고, 드롭다운의 개방 상태 관리가 성능에 미치는 영향이 크게 우려되지 않는다면, 방법 1이 더 나을 수 있을 것 같습니다.
반면에 코드의 단순성과 일관된 성능을 우선시한다면, 방법 2가 더 나은 선택일 것 같습니다.
굳이~~ 나의 상황에서 둘중에 하나를 고르자면 방법 2를 고를 것 같습니다.
제 경우는 버튼을 많이 누를 일이 없기 때문입니다.
하지만 이것은 모든 상황에 해당되는 것은 아닙니다.
버튼의 용도에 따라서 방법 1이 더 좋은 방법일 수 있습니다.
본인의 상황에 맞는 방법을 사용해보세요!
'빵부스러기' 카테고리의 다른 글
[빵부스러기] next.js 로 다국어 페이지 구현하기 0 (0) | 2024.02.27 |
---|---|
[빵부스러기] <a> 태그 target="_blank" (0) | 2024.02.09 |
[TypeScript 오류] 양방향 import 오류 - Dependency cycle detected. eslint(import/no-cycle) (0) | 2024.02.06 |
[React] Cannot import type declaration files. Consider importing '[파일이름]' instead of '@types/[파일이름]'.ts(6137) - @types 경로 오류 (0) | 2024.01.29 |
[빵 부스러기] React 폰트 적용 (0) | 2024.01.26 |