구성 요소 외부 이벤트를 처리하기 위한 사용자 지정 React 후크 만들기

이번 주말 사이드 프로젝트에서 나는 모달 구현에 대한 고전적인 사례를 가지고 있었습니다. 매일 처리해야 하는 대부분의 응용 프로그램에서 "100% 확신합니다"버튼을 누르기 전에 몇 가지 작업을 확인하거나 일부 변경 사항을 검토해야 하는 위치에 옵니다.

이것은 화면을 부분적으로 덮고 몇 가지 옵션을 제공하는 작은 보기인 모달에 대한 완벽한 케이스입니다. 대부분의 경우 오른쪽 상단 모서리에 있는 "닫기"버튼을 클릭하여 모달을 닫는 버튼이 있습니다. 그러나 사용자가 모달을 해제하도록 하는 더 좋은 방법은 상단에 있는 너무 작은 "x"를 누르지 않고 포커스가 있는 뷰 외부를 클릭하도록 하는 것입니다.

다음은 이 게시물에서 빌드할 모달 구성 요소의 실시간 구현입니다. Codesandbox 에서 사용해보십시오.



이와 같은 경우에는 기본 보기(이 경우 모달) 외부에서 클릭 또는 탭을 관찰해야 하므로 올바른 닫기 작업을 수행할 수 있습니다. 그러나 React?에서 어떻게 할 수 있습니까? 한 가지 방법은 다음과 같이 구성 요소에서 전역 클릭 핸들러를 구현하는 것입니다.

import React, { useRef, useEffect } from "react";

export default function Modal({onClose, ...props}) {
  const modalRef = useRef(null);

  useEffect(() => {

    const handleClick = (event) => {
      if (modalRef.current && !modalRef.current.contains(event.target)) {
        // Here you can close your modal.
        // how to close it, that is up to you
        // (e.g. removing components, changing routes)
        // in this case, I am calling a `onClose` function
        // passed down as a prop.
        console.log('Click happened outside. you can close now.')
        onClose()
      }
    }

    // Pointer events are more device agnostic
    // which are able to handle clicks on Desktops and Taps on mobile devices
    // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/pointerdown_event
    document.addEventListener("pointerdown", handleClick)
    // Once our component unmount or update, we must remove the event listener
    return () => document.removeEventListener("pointerdown", handleClick)

    // Use the modalRef as dependency for the useEffect hook
    // so whenever this reference changes, the listener will update
  }, [modalRef])

  return (
    <div ref={modalRef} className="my-modal">
      <div className="modal-header">
        Super important Action
      </div>
      <div className="modal-body">
        This is an important message. read it carefully.
      </div>
      <div className="modal-footer">
        <button>Cancel</button>
        <button>Ok</button>
      </div>
    </div>
  );
}


그러나 이 구현은 복제의 여지를 많이 남기지 않습니까? 다른 구성 요소에서 유사한 사례를 처리해야 하는 경우 동일한 클릭 해제 논리를 반복하게 될 것입니다. 우리는 커스텀 React hooks의 힘을 활용함으로써 그보다 더 잘 할 수 있습니다.

커스텀 React 후크와 로직 공유하기



제 생각에 후크는 React에서 가장 아름다운 기능 중 하나입니다. 애플리케이션에 강력한 기능을 제공하는 방식으로 구성 요소를 구성할 수 있습니다. React 자체는 useState , useEffect 및 기타 여러 가지와 함께 후크의 힘을 활용합니다.

그러나 React가 제공하는 후크에 국한되지 않고 자체 후크를 생성하여 앱 전체에서 매우 기능적인 방식으로 로직을 공유할 수 있습니다. 이전 모달 구성 요소에서 사용자 정의 훅으로 논리를 추출할 수 있습니다.

import { useEffect, useRef } from "react";

export function useClickAway(ref, onClickAway) {
  // Keep a mutable reference to click away callback
  // and change it every time the component using it changes
  // using 'useRef' here will make sure that we have a mutable
  // and single callback lying around.
  const callbackRef = useRef(onClickAway);
  useEffect(() => {
    callbackRef.current = onClickAway;
  }, [onClickAway]);

  // listen for click events on ref element
  // attaching a handler and calling the callback if necessary
  useEffect(() => {
    const onPointerDown = (event) => {
      if (ref.current && !ref.current.contains(event.target)) {
        callbackRef.current(event);
      }
    };
    document.addEventListener("pointerdown", onPointerDown);
    return () => {
      document.removeEventListener("pointerdown", onPointerDown);
    };
  }, [ref]);
}


우리의 커스텀 훅을 무너뜨릴 수 있습니다.
  • useClickAway 라는 일반 자바스크립트 함수를 선언하는 것으로 시작합니다. 이 함수는 두 개의 인수를 취합니다. Aref는 경계 "외부"클릭을 감시하려는 구성 요소에 대한 변경 가능한 참조입니다. 그리고 onClickAway 콜백은 외부 클릭을 감지하면 실행됩니다.
  • useClickAway 후크를 사용하여 useRef 콜백에 대한 참조를 생성했습니다. 이것은 나중에 사용할 useClickAway 호출에 의해 캡처된 useEffect 콜백에 대한 참조가 하나만 있는지 확인합니다.
  • 첫 번째 useEffect 호출에서 useClickAway 참조를 추적해야 합니다. 따라서 구성 요소가 useClickAway 참조를 업데이트하는 경우 사용자 지정 후크 내부의 내부 참조도 업데이트해야 합니다.
  • 두 번째 useEffect 호출에서 고무가 도로를 치는 곳입니다. 주의를 기울이면 이 호출은 모달 구성 요소에서 구현한 것과 정확히 동일합니다. 유일한 차이점은 callbackRef 함수에 대한 onClickAway 참조를 대신 호출한다는 것입니다. 이것은 뷰 외부에서 클릭이 발생하면 콜백의 올바른 참조를 호출하고 있는지 확인하기 위한 추가 검사입니다.

  • 그것이 제자리에 있으면 모달 구성 요소에서 어떻게 사용할 수 있습니까? 이제 코드가 어떻게 생겼는지 봅시다:

    import React, { useRef } from "react";
    import { useClickAway } from "./useClickAway";
    
    export default function Modal({ onClose }) {
      const modalRef = useRef(null);
    
      useClickAway(modalRef, () => {
        onClose();
      });
    
      return (
        <div className="shadow-overlay">
          <div ref={modalRef} className="my-modal">
            <div className="modal-header">Super important Action</div>
            <div className="modal-body">
              This is an important message. read it carefully.
            </div>
            <div className="modal-footer">
              <button onClick={onClose}>Cancel</button>
              <button>Ok</button>
            </div>
          </div>
        </div>
      );
    }
    


    이제 모달 구성 요소가 얼마나 깨끗한지 알 수 있습니까? 더 좋은 점은 useClickAway 후크를 재사용하는 것만으로도 앱 전체에서 동일한 논리를 재사용할 수 있다는 것입니다. 멋지지 않아?

    Here is the link 이 블로그 게시물에서 구축한 Codesandbox 데모에 추가합니다. 자유롭게 복사하여 앱에서 사용하세요.

    좋은 웹페이지 즐겨찾기