Source

useModalManager.jsx

import { useState } from "react"

/**
 * Custom hook that manages the state of modals on a page.
 * @param {Object.<string, JSX.Element>} modalConfig 
 * @category Hooks
 * @module useModalManager
 * @example
 * import Modal1 from './Modal1'
 * import Modal2 from './Modal2'
 * 
 * const Page = () => {
 *     const { openModal, renderModal } = useModalManager({
 *         "modal1": Modal1
 *         "modal2": Modal2
 *     })
 * 
 *     const modal1Props = {
 *         data: { id: 1, name: "John Doe"},
 *         otherData: { id: 1, name: "Jane Doe"}
 *     }
 * 
 *     return (
 *         <div>
 *             <button 
 *                 // Pass in the props for the component when you open the modal
 *                 onClick={() => openModal("modal1", {
 *                     data: modal1Props["data"]
 *                     otherData: modal1Props["otherData"]
 *                 })}
 *             >
 *                 Open Modal 1
 *             </button>
 *             <button
 *                 // If no props are necessary for the component, don't pass in anything
 *                 onClick={() => openModal("modal2")}
 *             >
 *                 Open Modal 2
 *             </button>
 *             {renderModal()} // Then use the `renderModal` function to render
 *         </div>
 *     )
 * }
 */
const useModalManager = (modalConfig) => {
    const [modalState, setModalState] = useState({ type: null, props: {} })

    /**
     * Method to open a modal of a specific type with optional props
     * 
     * NOTE: Ensure the modal being opened is present in `modalConfig`
     * 
     * @param {string} type Type of modal to open
     * @param {object} props Props to be passed to the modal component
     */
    const openModal = (type, props = {}) => {
        if (!modalConfig[type]) {
            console.warn(`Modal type "${type}" is not defined in the configuration`)
            return
        }
        setModalState({ type, props })
    }

    const closeModal = () => setModalState({ type: null, props: {} })

    /**
     * Renders the modal dictated by the current state
     * 
     * @returns JSX.Element
     */
    const renderModal = () => {
        const { type, props } = modalState
        if (!type || !modalConfig[type]) return null

        const ModalComponent = modalConfig[type]
        return (
            <ModalComponent
                open={true}
                onClose={closeModal}
                {...props}
            />
        )
    }

    const isModalOpen = (type) => modalState.type === type

    return {
        openModal,
        closeModal,
        renderModal,
        isModalOpen
    }
}

export default useModalManager