@vegatality/react-hook-modal
v0.1.25
Published
A simple modal hook for React
Downloads
3
Maintainers
Readme
A simple modal hook for React
This is a simple modal hook for React. It is a custom hook that allows you to create a modal with a simple API.
TOC
- Installation
- Usage
- Types
Installation
# npm
npm i @vegatality/react-hook-modal
# yarn
yarn add @vegatality/react-hook-modal
# pnpm
pnpm add @vegatality/react-hook-modal
Usage
1. Local State Layer
import { useModalList, type ModalComponent } from '@vegatality/react-hook-modal';
// const SomeModal = ({ closeModal, modalRef }: ModalComponentProps<{ name: string }>) => {
const SomeModal: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
return (
<div ref={modalRef}>
<h1>Some Modal</h1>
<p>{name}</p>
<button onClick={closeModal}>Close</button>
</div>
);
};
const App = () => {
const { ModalComponentList, openModal, closeModal, watch, destroy, ... } = useModalList();
return (
<div>
<ModalComponentList />
<button onClick={() => openModal({ modalKey: ['some'], ModalComponent: SomeModal, modalProps: { name: 'some' }, options: { resistBackgroundClick: true } })}>
Open Modal 1
</button>
</div>
);
};
1-1. with Context API
import { useModalList, useModalContext, ModalProvider, type ModalComponent } from '@vegatality/react-hook-modal';
const SomeModal: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
return (
<div ref={modalRef}>
<h1>Some Modal</h1>
<p>{name}</p>
<button onClick={closeModal}>Close</button>
</div>
);
};
const ChildComponent = () => {
const { openModal } = useModalContext();
return (
<button onClick={() => openModal({ modalKey: ['some'], ModalComponent: SomeModal, modalProps: { name: 'some' } })}>
Open Modal 1
</button>
);
};
const App = () => {
const methods = useModalList();
const { ModalComponentList, openModal } = methods;
return (
<div>
<ModalComponentList />
<ModalProvider {...methods}>
<ChildComponent />
</ModalProvider>
</div>
);
};
2. Global State Layer
import {
useGlobalModalList,
GlobalModalList,
GlobalModalListProvider,
type ModalComponentProps,
} from '@vegatality/react-hook-modal';
const SomeModal = ({ closeModal, modalRef, name }: ModalComponentProps<{ name: string }>) => {
return (
<div ref={modalRef}>
<h1>Some Modal</h1>
<p>{name}</p>
<button onClick={closeModal}>Close</button>
</div>
);
};
const App = () => {
const { openGlobalModal } = useGlobalModalList(); // useGlobalModalList hook uses useContext hook under the hood
return (
<button
onClick={() => openGlobalModal({ modalKey: ['some'], ModalComponent: SomeModal, modalProps: { name: 'some' } })}
>
Open Modal 1
</button>
);
};
const Main = () => {
return (
<GlobalModalListProvider>
<App />
<GlobalModalList />
</GlobalModalListProvider>
);
};
3. Hierarchical Modal Key Management
You can manage the modal key hierarchically. If you pass an array as a modal key, it will be managed hierarchically.
const openModal1 = () => {
openModal({
modalKey: ['test'],
ModalComponent: TestModal,
options: { resistBackgroundClick: true },
});
};
const openModal2 = () => {
openModal({
modalKey: ['test', 2],
ModalComponent: TestModal,
options: { resistBackgroundClick: [['test']] }, // This will resist background click until ['test'] modal is closed
});
};
const openGlobalModal1 = () => {
openGlobalModal({
modalKey: ['test', 2, { id: 1 }],
ModalComponent: TestModal,
options: { resistBackgroundClick: false },
});
};
4. Can open Multiple Modals
You can open multiple modals with openModal
api from useModalList
hook.
import { useModalList, type ModalComponent } from '@vegatality/react-hook-modal';
const SomeModal: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
return (
<div ref={modalRef}>
<h1>Some Modal</h1>
<p>{name}</p>
<button onClick={closeModal}>Close</button>
</div>
);
};
const SomeModal2: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
return (
<div ref={modalRef}>
<h1>Some Modal2</h1>
<p>{name}</p>
<button onClick={closeModal}>Close</button>
</div>
);
};
const App = () => {
const { ModalComponentList, openModal, closeModal, watch, destroy, ... } = useModalList();
return (
<div>
<ModalComponentList />
<button onClick={() => openModal({ modalKey: ['some'], ModalComponent: SomeModal, modalProps: { name: 'some' }, options: { resistBackgroundClick: true } })}>
Open Modal 1
</button>
<button onClick={() => openModal({ modalKey: ['some2'], ModalComponent: SomeModal2, modalProps: { name: 'some2' }, options: { resistBackgroundClick: false, scrollable: false } })}>
Open Modal 2
</button>
</div>
);
};
5. Close Modal API
You can close the modal using the modal key that was passed when it was opened.
const { ModalComponentList, openModal, closeModal, watch, destroy, ... } = useModalList();
closeModal({ modalKey: ['some'] });
If you want to close all modals, you can use the destroy
method.
useEffect(
() => () => {
destroy();
},
[],
);
5-1. Default Closing Modal Order with Options
If multiple modals are open, they will close in the order they were opened, starting with the most recently opened modal.
5-2. Handling Modals with Options
If the option value of the most recently opened modal is true
or if there is a specified modalKey
to resist, it will find and close the most recent modal with the specified option value set to false that was opened before it.
For example, if the resistBackgroundClick
option value of the most recently opened modal is true
or if the modal with the specified modalKey
is still open, it will find and close the earlier opened modal with the resistBackgroundClick
value set to false
when the background is clicked.
5-3. Resist other modals
You can prevent the modal from closing if the specified modal is open.
Just specify the modalKey of the modal you want to resist in the options
object when opening the modal.
import { useModalList, type ModalComponent } from '@vegatality/react-hook-modal';
const SomeModal: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
return (
<div ref={modalRef}>
<h1>Some Modal</h1>
<p>{name}</p>
<button onClick={closeModal}>Close</button>
</div>
);
};
const SomeModal2: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
return (
<div ref={modalRef}>
<h1>Some Modal2</h1>
<p>{name}</p>
<button onClick={closeModal}>Close</button>
</div>
);
};
const App = () => {
const { ModalComponentList, openModal, closeModal, watch, destroy, ... } = useModalList();
return (
<div>
<ModalComponentList />
<button onClick={() => openModal({ modalKey: ['some', 1], ModalComponent: SomeModal, modalProps: { name: 'some' }, options: { resistBackgroundClick: true } })}>
Open Modal 1
</button>
<button onClick={() => openModal({ modalKey: ['some2'], ModalComponent: SomeModal2, modalProps: { name: 'some2' }, options: { resistBackgroundClick: [['some', 1]] } })}> /* 👈 this will resist background click until ['some', 1] modal is closed */
Open Modal 2
</button>
</div>
);
};
5-4. Default Modal Options
You can set the default options for all modals. These default options are applied to all modals that are opened without specifying their own options. However, the options specified when opening each modal will override the default options.
import { useModalList, type ModalComponent } from '@vegatality/react-hook-modal';
const SomeModal: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
return (
<div ref={modalRef}>
<h1>Some Modal</h1>
<p>{name}</p>
<button onClick={closeModal}>Close</button>
</div>
);
};
const App = () => {
const { ModalComponentList, openModal, closeModal, watch, destroy, ... } = useModalList({
mode: {
resistBackgroundClick: true, // set default option
}
});
return (
<div>
<ModalComponentList />
<button onClick={() => openModal({ modalKey: ['some'], ModalComponent: SomeModal, modalProps: { name: 'some' }, options: { resistBackgroundClick: false } })}> /* 👈 this will override default option */
Open Modal 1
</button>
</div>
);
};
5-5. Close Modal with Hierarchical Modal Key
You can close the modal with a hierarchical modal key.
If you want to close a modal with an exactly matched modal key, you must set the exact
option to true
.
const killTestModals = () => {
closeModal({ modalKey: ['test'] }); // This will close modals with key like ['test'], ['test', 1], ['test', 2], ['test', 2, { once: true } ], and etc.
};
const killTestModalExactly = () => {
closeModal({ modalKey: ['test'], exact: true }); // This will only close modal with key ['test']
};
6. Pass modalRef
Prop
modalRef
is a reference to the modal element.
You have to pass the modalRef
to the element ref.
It determine the valid area of a modal.
import { ModalComponent } from '@vegatality/react-hook-modal';
const SomeModal: ModalComponent<{ name: string }> = ({ closeModal, modalRef, name }) => {
return (
<div className='modal_boundary' ref={modalRef}>
<h1>Some Modal</h1>
<p>{name}</p>
<button onClick={closeModal}>Close</button>
</div>
);
};
If you don't pass the modalRef
, the modal will not close even if you set resistBackgroundClick
to false (default).
This is because, without modalRef
, we can't determine the valid boundary of the modal.
However, the modal will close properly when the ESC
key is pressed, regardless of whether the modalRef
is passed.
7. useToggleModal
Hook
You can use the useToggleModal
hook to toggle the modal instead of using useModalList
hook.
It is a simple hook that returns the isModalOpen
, modalRef
, and toggleModal
function that only controls a single modal.
import { useToggleModal } from '@vegatality/react-hook-modal';
function App() {
const { isModalOpen, modalRef, toggleModal } = useToggleModal({
initialValue: false,
openModalOptions: { resistBackgroundClick: true },
});
return (
<div>
<button onClick={toggleModal}>Toggle Modal</button>
{isModalOpen && (
<div ref={modalRef}>
<h1>Modal</h1>
<button onClick={toggleModal}>Close Modal</button>
</div>
)}
</div>
);
}
Types
Providing types for the modal component. You can use them as shown below.
import type { ModalComponent, ModalComponentProps } from '@vegatality/react-hook-modal';
export const TestModal = ({ name, closeModal, modalRef }: ModalComponentProps<{ name: string }>) => {
return (
<div ref={modalRef}>
<h1>Test Modal</h1>
<p>{name}</p>
<button type='button' onClick={closeModal}>
Close Modal
</button>
</div>
);
};
export const TestModal2: ModalComponent<{ name: string }> = ({ name, closeModal, modalRef }) => {
return (
<div ref={modalRef}>
<h1>Test Modal</h1>
<p>{name}</p>
<button type='button' onClick={closeModal}>
Close Modal
</button>
</div>
);
};
However, you don't have to use the provided modal component types(e.g. ModalComponent, ModalComponentProps). You can use your own defined types.
import { ModalRef } from '@vegatality/react-hook-modal';
interface SomeModalProps {
name: string;
modalRef: ModalRef;
}
const SomeModal = ({ name, modalRef }: SomeModalProps) => {
return (
<div ref={modalRef}>
<h1>Test Modal</h1>
<p>{name}</p>
</div>
);
};
const App = () => {
const { ModalComponentList, openModal, closeModal, watch, destroy, ... } = useModalList();
return (
<div>
<ModalComponentList />
<button onClick={() => openModal({ modalKey: ['some'], ModalComponent: SomeModal, modalProps: { name: 'some' }, options: { resistBackgroundClick: true } })}>
Open Modal 1
</button>
<button onClick={() => openModal({ modalKey: ['some2'], ModalComponent: SomeModal2, modalProps: { name: 'some2' }, options: { resistBackgroundClick: false, scrollable: false } })}>
Open Modal 2
</button>
<button onClick={() => closeModal({ modalKey: ['some2'] })}>Close Modal 2</button>
</div>
);
};