[React] 버튼 누르면 필터 메뉴 나타나는 기능 구현하기

728x90

[React] 버튼 누르면 필터 메뉴 나타나는 기능 구현하기


 

1. 설명

Mui라이브러리를 활용하여 특정 버튼을 누르면 미리 지정한 옵션대로 필터 메뉴가 열람되고, 특정 버튼이나 메뉴 바깥을 클릭하면 필터 메뉴가 닫히는 기능을 구현했다.

 

npm install @mui/icons-material
npm install @mui/material

 

import FilterAltIcon from '@mui/icons-material/FilterAlt';
import { IconButton } from '@mui/material';

const Test = () => {
  const FilterButton = () => {
    return (
      <>
        <IconButton
          sx={{
            color: '#339af0',
            backgroundColor: '#74c0fc',
            borderRadius: '8px',
            border: 'none',
            padding: '3px',
          }}
        >
          <FilterAltIcon />
        </IconButton>
      </>
    );
  };

  return (
    <>
      <FilterButton />
    </>
  );
};

export default Test;

먼저 임의의 버튼을 하나 만들었다.

 

버튼을 누르면 마우스 포인터 왼쪽 아래 방향으로 필터 메뉴가 나타날 수 있도록 만들기 위해 다음과 같은 코드를 추가했다.

 

const FilterOptions = ({ anchorEl, handleClose }) => {
  // 필터 옵션 설정
  return (
    <Menu
      anchorReference="anchorPosition"
      anchorPosition={anchorEl}
      open={Boolean(anchorEl)} // 필터메뉴 표시 여부 결정
      onClose={handleClose} // anchorEl을 null로 설정하여 open이 false가 되도록 유도하는 콜백함수를 실행시킴
      keepMounted // true시에 DOM에서 메뉴의 하위 요소를 유지하도록 함으로써 성능향상에 기여
      anchorOrigin={{
        vertical: 'bottom',
        horizontal: 'right',
      }}
      transformOrigin={{
        vertical: 'top',
        horizontal: 'right',
      }}
    >
      <MenuItem onClick={handleClose}>최근 생성순</MenuItem>
      <MenuItem onClick={handleClose}>이름순</MenuItem>
      {/* 클릭하면 메뉴가 닫히도록 해야함 */}
    </Menu>
  );
};

anchorEl: 메뉴의 위치를 설정하는 top, left등의 속성을 보유한 prop이다.

handleClose: 메뉴 항목을 클릭하거나 메뉴가 닫힐 때 anchorEl 상태를 null로 설정하는 콜백함수다.

 

anchorPosition: 메뉴의 위치를 정했던 anchorEl prop을 전달받는 속성이다.

anchorReference: anchorPosition을 기반으로 메뉴를 배치하는 역할을 한다.

(anchorEl => anchorPosition => anchorReference)

 

open: 메뉴의 표시여부를 결정한다. anchorEl값이 null이 아닌 경우에만 메뉴가 표시되도록 한다.

onClose: null로 설정될 때 메뉴가 닫히도록 하는 속성이다.

 

keepMounted: true를 디폴트로 가지며, 메뉴가 닫혀도 DOM에 메뉴의 하위 요소를 유지하도록 해 성능 향상 기여한다.

 

anchorOrigin: anchorEl 또는 anchorPosition을 기준으로 메뉴의 기준점을 결정한다. vertical, horizontal 속성을 가지고 있다.

 anchorOrigin={{
        vertical: 'bottom',
        horizontal: 'right',
      }}

위의 코드를 통해 메뉴 오른쪽 상단 모서리를 필터 위치의 기준점으로 정한다.

 

transformOrigin: 메뉴의 표시와 사라지는 애니메이션의 원점을 결정한다.

transformOrigin={{
        vertical: 'bottom',
        horizontal: 'right',
      }}

마찬가지로 오른쪽 상단 모서리에서 애니메이션이 시작되도록 설정된다.

 

이 두 값을 어떻게 조절하느냐에 따라 마우스 포인터를 기점으로 어느 방향으로 필터가 열리게 되는지 결정된다.

 

const Test = () => {

  const [anchorEl, setAnchorEl] = useState(null); 

  const handleClick = (event) => {
    setAnchorEl({ top: event.clientY, left: event.clientX });
  };

  const handleClose = (event) => {
    setAnchorEl(null);
  };
  
  const FilterButton = () => {
    return (
      <>
        <IconButton
          onClick={handleClick}
          sx={{
            color: '#339af0',
            backgroundColor: '#74c0fc',
            borderRadius: '8px',
            border: 'none',
            padding: '3px',
          }}
        >
          <FilterAltIcon />
        </IconButton>
        <FilterOptions anchorEl={anchorEl} handleClose={handleClose} />
      </>
    );
  };

  return (
    <>
      <FilterButton />
    </>
  );
};

export default Test;
const [anchorEl, setAnchorEl] = useState(null); 

  const handleClick = (event) => {
    setAnchorEl({ top: event.clientY, left: event.clientX });
  };

마우스 포인터가 클릭될 때의 위치를 기준으로 state에 anchor의 상태를 저장한다.

 

const handleClose = (event) => {
    setAnchorEl(null);
  };
<FilterOptions anchorEl={anchorEl} handleClose={handleClose} />

필터 메뉴의 특정 요소 또는 필터 메뉴 바깥을 클릭하게 되면 onClose가 실행된다.

onClose에 handleClose 메서드가 실행되도록 전달하고, 이 메서드가 실행되면 anchorEl이 null이 되도록 설정한다.

anchorEl이 null이 되면 open={Boolean(anchorEl)}에 의해 false값이 전달된다. 따라서 메뉴가 닫히도록 설정된다.

 

결국 필터를 여는 버튼을 클릭하면 마우스 포인트의 위치에 대한 정보가 넘어감으로써 메뉴가 open되고,

메뉴의 특정 요소 또는 메뉴 바깥을 클릭할 시 open에 false값이 전달되면서 메뉴가 닫히게 되는 것이다.

 

2. 전체 코드

import FilterAltIcon from '@mui/icons-material/FilterAlt';
import { IconButton, Menu, MenuItem } from '@mui/material';
import { useState } from 'react';

const FilterOptions = ({ anchorEl, handleClose }) => {
  // 필터 옵션 설정
  return (
    <Menu
      anchorReference="anchorPosition"
      anchorPosition={anchorEl}
      open={Boolean(anchorEl)} // 필터메뉴 표시 여부 결정
      onClose={handleClose} // anchorEl을 null로 설정하여 open이 false가 되도록 유도하는 콜백함수를 실행시킴
      keepMounted // true시에 DOM에서 메뉴의 하위 요소를 유지하도록 함으로써 성능향상에 기여
      anchorOrigin={{
        vertical: 'bottom',
        horizontal: 'right',
      }}
      transformOrigin={{
        vertical: 'top',
        horizontal: 'right',
      }}
    >
      <MenuItem onClick={handleClose}>최근 생성순</MenuItem>
      <MenuItem onClick={handleClose}>이름순</MenuItem>
      {/* 클릭하면 메뉴가 닫히도록 해야함 */}
    </Menu>
  );
};

const Test = () => {

  const [anchorEl, setAnchorEl] = useState(null); 

  const handleClick = (event) => {
    setAnchorEl({ top: event.clientY, left: event.clientX });
  };

  const handleClose = (event) => {
    setAnchorEl(null);
  };
  
  const FilterButton = () => {
    return (
      <>
        <IconButton
          onClick={handleClick}
          sx={{
            color: '#339af0',
            backgroundColor: '#74c0fc',
            borderRadius: '8px',
            border: 'none',
            padding: '3px',
          }}
        >
          <FilterAltIcon />
        </IconButton>
        <FilterOptions anchorEl={anchorEl} handleClose={handleClose} />
      </>
    );
  };

  return (
    <>
      <FilterButton />
    </>
  );
};

export default Test;
728x90

'React' 카테고리의 다른 글

[React] 커스텀 훅 만들기  (0) 2023.08.25
[React] 스프레드 연산자와 상태 업데이트, 불변성  (0) 2023.02.02