본문 바로가기

코드 기록

[React] useClickOutside hook

728x90

React 로 개발하던 중, 특정 영역 밖을 클릭하면 떠있던 모달이 내려가거나 상태를 바꿔줘야 하는 경우가 발생했습니다. 

 

이 때 물론 useState를 사용하거나 useRef를 사용해 바로 구현해도 되지만 hook으로 빼면 코드가 좀 더 간결해질 수 있습니다.

 

//useClickOutside.ts
import { useEffect, MutableRefObject } from 'react';

function useClickOutside<T extends HTMLElement>(ref: MutableRefObject<T | null>, onClickOutside: () => void) {
  useEffect(() => {
    /**
     * Invoke Function onClick outside of element
     */
    function handleClickOutside(event: MouseEvent) {
      if (ref.current && !ref.current.contains(event.target as Node)) {
        onClickOutside();
      }
    }

    // Bind
    document.addEventListener('click', handleClickOutside);
    return () => {
      // dispose
      document.removeEventListener('click', handleClickOutside);
    };
  }, [ref, onClickOutside]);
}

export default useClickOutside;
//Component.tsx
import useClickOutside from 'hooks/useClickOutside';
import { useRef, useState } from 'react';

export default function Component() {
  const [showModal, setShowModal] = useState(false);
  const compRef = useRef<HTMLInputElement>(null);
  useClickOutside(compRef, () => {
    setShowModal(false);
  });

  return <div ref={compRef}>...</div>;
}

 

위처럼 사용할 수 있습니다.

만약 클릭이 허용되는 (눌러도 상태가 변하지 않아야 하는) 영역이 많다면 ref 를 배열로 받도록 수정해야 합니다.

import { useEffect, MutableRefObject } from 'react';

function useClickOutside<T extends HTMLElement>(refs: Array<MutableRefObject<T | null>>, onClickOutside: () => void) {
  useEffect(() => {
    /**
     * Invoke Function onClick outside of element
     */
    function handleClickOutside(event: MouseEvent) {
      let refNotClickedCnt = 0;
      for (const ref of refs) {
        if (ref.current && !ref.current.contains(event.target as Node)) {
          refNotClickedCnt += 1;
        }
      }
      if (refNotClickedCnt === refs.length) {
        onClickOutside();
      }
    }

    // Bind
    document.addEventListener('click', handleClickOutside);
    return () => {
      // dispose
      document.removeEventListener('click', handleClickOutside);
    };
  }, [refs, onClickOutside]);
}

export default useClickOutside;

 

import useClickOutside from 'hooks/useClickOutside';
import { useRef, useState } from 'react';

export default function Component() {
  const [showModal, setShowModal] = useState(false);
  const compRef = useRef<HTMLInputElement>(null);
  const inputRef = useRef<HTMLInputElement>(null);
  useClickOutside([compRef, inputRef], () => {
    setShowModal(false);
  });

  return (
    <div>
      <div ref={compRef}>{}</div>
      <input ref={inputRef}>{}</input>;
    </div>
  );
}

 

 

'코드 기록' 카테고리의 다른 글

[flask-cors] 재설치  (0) 2024.08.24
개발자 도구 방지 코드 우회하기  (0) 2024.08.06
[Pyqt5] QMainWindow 배경 이미지 설정하기  (0) 2024.04.14
Why & What (is) Spidergen ?  (0) 2024.03.30
Restudy Series. Javascript (2)  (0) 2024.03.30