위와 같은 X 버튼이 달린 공통 Input 컴포넌트를 개발하였다.
X 버튼의 기능 명세는 다음과 같다.
- Input이 포커스 될 때에만 X 버튼이 나타나야 한다.
- X 버튼을 클릭하면 Input에 입력되어 있던 값이 초기화 되어야 한다.
1번 기능을 위해 다음과 같이 간단하게 구현했다
.cm-input:focus + .x-btn {
display: block;
}
음~ 예상대로 Input이 focus 될 경우 X 버튼아 잘 나타났고, blur 될 경우에 잘 사라졌기 때문에 1번을 완료했다.
하지만 문제는 2번이었다.
X 버튼이 onClick 이벤트가 발생하지 않았다.
문제의 코드를 보자.
const inputRef = useRef<HTMLInputElement>(null);
const [focused, setForcused] = useState(false);
const clearInput = () => {
setValue("");
};
const handleBlur = () => {
setForcused(false);
};
// tsx
//....
<input value={value ? value : ""}
onFocus={() => setForcused(true)}
onChange={(e) => setValue(e.target.value)}
ref={inputRef}
placeholder={placeholder}
/>
{focused && (
<div
className=" z-30 x-btn absolute top-[50%] translate-y-[-50%] cursor-pointer right-[10px]"
onClick={clearInput}
>
<Image
alt="x"
src={"/images/icons/x.svg"}
height={24}
width={24}
/>
</div>
)}
문제가 보이는가?
clearInput 콜백 함수가 작동하지 않는 이유 말이다.
이유는 X 버튼이 Input 요소 위에 absolute로 있었기 때문에
X 버튼 클릭시 엄연히 Input가 Blur 될 수 있는 영역이었기 때문이다.
쉽게 말해서 눈으로 봤을 때 같은 영역 안에 있다고 진짜 같은 부분에 있는 것이 아니라고 이해했다.
그래서 X 버튼을 클릭했을때 X버튼의 onClick 함수보다 handleBlur 함수가 더 먼저 실행되어 focused가 false 이므로 버튼이 먼저 사라지게 된다.
그래서 블러 이벤트 처리를 지연시키는 방법으로 다음과 같이 해결할 수 있었다.
const inputRef = useRef<HTMLInputElement>(null);
const [focused, setForcused] = useState(false);
const clearInput = () => {
setValue("");
};
const handleBlur = () => {
setTimeout(() => {
setForcused(false);
}, 10);
};
// tsx
//....
<input value={value ? value : ""}
onFocus={() => setForcused(true)}
onBlur={handleBlur}
onChange={(e) => setValue(e.target.value)}
ref={inputRef}
placeholder={placeholder}
/>
{focused && (
<div
className=" z-30 x-btn absolute top-[50%] translate-y-[-50%] cursor-pointer right-[10px]"
onClick={clearInput}
>
<Image
alt="x"
src={"/images/icons/x.svg"}
height={24}
width={24}
/>
</div>
)}