element 바깥을 클릭 할때

원하는 것

요소 바깥을 클릭하면 모달이 꺼지는 것.

필요한 것

요소 바깥을 클릭하는 이벤트.

구현

안쪽 요소에 대한 ref 와 바깥을 클릭했을때 호출되는 handler 를 받는 custom hook 을 구상하고, 테스트를 작성해 주었다.

import { render } from '@testing-library/react';

import userEvent from '@testing-library/user-event';

import { useRef } from 'react';

import useClickOutside from './useClickOutside';

describe('useClickOutside', () => {
  function Component({ onClickOutside }: {
    onClickOutside: any
  }) {
    const insideElementRef = useRef(null);

    useClickOutside(insideElementRef, onClickOutside);

    return (
      <div id="outside">
        <p>바깥</p>
        <div id="inside" ref={insideElementRef}>
          <p></p>
        </div>
      </div>
    );
  }

  it('listens click outside event', () => {
    const handleClickOutside = jest.fn();

    const { getByText } = render(<Component onClickOutside={handleClickOutside} />);

    userEvent.click(getByText('안'));

    expect(handleClickOutside).not.toBeCalled();

    userEvent.click(getByText('바깥'));

    expect(handleClickOutside).toBeCalled();
  });
});

구현은 단순하다. Dom Node 는 contains 라고 하는 메서드를 가지고 있는데, 이를 통해서 다른 요소가 자식 요소인지 알 수 있다.
(https://developer.mozilla.org/en-US/docs/Web/API/Node/contains)

import { useEffect } from 'react';

export default function useClickOutside(ref, handler) {
  useEffect(() => {
    const listener = (e) => {
      if (!ref.current || ref.current.contains(e.target)) {
        return;
      }

      handler();
    };

    document.addEventListener('mousedown', listener);

    return () => {
      document.removeEventListener('mousedown', listener);
    };
  });
}

좋은 웹페이지 즐겨찾기