웹/React

React 드롭다운 blur 이벤트 click 이벤트 올바르게 처리하기

공대생철이 2023. 8. 11. 16:13
728x90

 

import React, { useState } from "react";
import Arrow from "../icon/dropdown-arrow";

type DropdownItem = {
  name: string;
  id: string;
};
type DropdownProps = {
  items: DropdownItem[];
};

function DropDown({ items }: DropdownProps) {
  const [isDropdownOpen, setIsDropdownOpen] = useState<boolean>(false);
  const [selectedItem, setSelectedItem] = useState<string>("선택");

  return (
    <div
      className="bg-[#f5f5f5] rounded-[5px]"
      onBlur={() => {
        setIsDropdownOpen(false);
      }}
    >
      <button
        onClick={() => {
          setIsDropdownOpen((prev) => !prev);
        }}
        className="w-[240px]  bg-[#F5F5F5] h-[50px] rounded-[5px] flex items-center justify-between p-[16px]"
      >
        <div className={`${selectedItem === "선택" && "text-[#a0a0a0]"} w-fit`}>{selectedItem}</div>
        <div className={`${isDropdownOpen && "rotate-180"}`}>
          <Arrow />
        </div>
      </button>

      <div
        className={`${
          isDropdownOpen ? "" : "hidden"
        } absolute bg-[#f5f5f5] w-[240px] rounded-bl-[5px] rounded-br-[5px]  `}
      >
        {items.map((item) => (
          <div
            key={item.id}
            className="h-[50px] text-left p-[16px] cursor-pointer"
            onClick={() => {
              setSelectedItem(item.name);
              setIsDropdownOpen(false);
            }}
          >
            {item.name}
          </div>
        ))}
      </div>
    </div>
  );
}

export default DropDown;

 

만들어본 드롭다운 컴포넌트의 코드는 다음과 같다.

 

기본 버튼을 클릭하면 메뉴가 펼쳐지고 메뉴 중에 하나를 클릭하면 state에 저장한 후 드롭다운이 닫히는 구조이다.

여기서 드롭다운이 펼쳐져 있을 때 밖의 화면을 클릭하면 자동으로 닫힐 수 있도록 설정하려고 했는데...

 

아래와 같은 상황이 펼쳐졌다.

상황파악을 해보자면 드롭다운 메뉴의 onClick에 대한 함수가 적용되는 것이 아니라 onBlur에 대한 함수가 적용되어 선택값이 state에 반영되지 않고 드롭다운만 계속 닫히게 된다.

 

어떻게 된 일이지...?

 

구글링을 해보니 답을 찾았다...!

어떤 태그(컴포넌트)에 대해서 이벤트에 대한 우선순위가 있었다. 

위쪽일수록 우선순위가 높은 이벤트이다.

나는 onBlur와 onClick 간의 우선순위 비교가 필요했는데 blur > focus > mouseup> click 이기 때문에 blur 이벤트가 우선시 되어 실행되게 된다. onBlur의 우선순위가 더 높기 때문에 onClick 대신 onBlur의 함수가 대신 실행된 것이다.

 

클릭과 거의 흡사한 역할을 하지만 우선순위가 더 높은 mousedown으로 코드를 바꿔보았다.

<div
	key={item.id}
	className="h-[50px] text-left p-[16px] cursor-pointer"
	onMouseDown={() => {
	setSelectedItem(item.name);
	setIsDropdownOpen(false);
	}}
	>
	{item.name}
</div>

성공적으로 원하는 동작을 만들었다.

메뉴가 클릭될 때마다 state에 반영도 잘 되고 있고 blur 이벤트일 때 state값을 유지한 채로 메뉴가 잘 닫히는 것도 확인할 수 있었다.

 

 

오늘 배운 것

이벤트에도 우선순위가 있다. 이벤트 종류만 무작정 외워서 쓸 생각 하지 말고 우선순위가 있다는 걸 명심하고 적절하게 사용하자.

 

참고:

https://velog.io/@ahn0min/React-Blur-%EC%9D%B4%EB%B2%A4%ED%8A%B8%EA%B0%80-Click-%EC%9D%B4%EB%B2%A4%ED%8A%B8%EB%B3%B4%EB%8B%A4-%EB%A8%BC%EC%A0%80-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0 

728x90