본문 바로가기
코딩 정보/React

[React] 재활용성이 좋은 modal 만들기

by 꽁이꽁설꽁돌 2024. 8. 10.
728x90
반응형

목차

    범용성이 좋은 modal component

    import { useEffect, useRef } from "react";
    import { createPortal } from "react-dom";
    
    function Modal({ children, className = "", open, onClose }) {
      const dialog = useRef();
      useEffect(() => {
        
        //참조값을 지정해 놓기
        const modal = dialog.current;
        if (open) {
          modal.showModal();
        }
        return () => {
          modal.close();
    
        };
      }, [open]);
    
      return createPortal(
        <dialog ref={dialog} className={`modal ${className}`} onClose={onClose}>
          {children}
        </dialog>,
        document.getElementById("modal")
      );
    }
    
    export default Modal;

     

    컴포넌트 설명

    1. children과 className을 통해 css 및 컴포넌트의 활용성을 높였다.
    2. open이라는 상태를 전달함으로써 모달이 닫히고 열리는 것을 구현하였고 clear함수를 통해 닫히는 기능이 만들어지는 것을 가져가면 좋을 것 같다.
    3. createPortal을 이용해서 dom의 modal에 직접 연결해 주었다.
    4. onClose를 쓴 이유는 esc를 누르면 외부에서 modal은 닫히지만 state자체는 변경되지 않았기 때문에 다시 모달이 안열리는 경우를 막기 위해서이다.

     

    modal ContextProvider

    아래 코드는 이러한 모달을 이용한 컴포넌트들을 전역적으로 관리하기 위해 만든 contextProvider이다.

    import { createContext, useState } from "react";
    
    export const userProgressContext = createContext({
      progress: "",
      showCart: () => {},
      hideCart: () => {},
      showCheckout: () => {},
      hideCheckout: () => {},
    });
    
    export function UserProgressContextProvider({ children }) {
      const [userProgress, setUserProgress] = useState("");
    
      function showCart() {
        setUserProgress("cart");
      }
      function hideCart() {
        setUserProgress("");
      }
      function showCheckout() {
        setUserProgress("checkout");
      }
      function hideCheckout() {
        setUserProgress("");
      }
      const userProgressCtx = {
        progress: userProgress,
        showCart,
        hideCart,
        showCheckout,
        hideCheckout,
      };
    
      return (
        <userProgressContext.Provider value={userProgressCtx}>
          {children}
        </userProgressContext.Provider>
      );
    }

     

    전역적으로 설정한 모달 컴포넌트의 활용

    import { useContext } from "react";
    import Button from "./UI/Button";
    import { userProgressContext } from "./store/UserProgressContext";
    import Modal from "./Modal";
    import CartItem from "./CartItem";
    function Cart() {
    
    
      const userProgressCtx = useContext(userProgressContext);
    
      function handleHideCart() {
        userProgressCtx.hideCart();
      }
    
    
    
      return (
        <Modal
          className="cart"
          open={userProgressCtx.progress === "cart"}
          onClose={userProgressCtx.progress === "cart" ? handleHideCart : null}
        >
        <Button onClick={handleCheckout}>go to Checkout</Button>
          
        </Modal>
      );
    }
    
    export default Cart;

     

    위 코드에서의 주의사항이다.

          onClose={userProgressCtx.progress === "cart" ? handleHideCart : null}

     

    이렇게 코드를 짠 이유는 userProgressCtx.progress가 checkout일 때는 onClose의 기능을 비활성화해서 checkout이 열리게끔 만들어야하기 때문이다.

     

    반응형