The easiest way to interact with the user through modal dialogs is to use browser APIs like window.alert
, window.confirm
, window.prompt
. There is only one problem. You cannot customize their appearance.
<body>
<div>
<button class="trigger">I want to drink something!</button>
<div class="result"></div>
</div>
<script>
const resultElement = document.querySelector(".result");
const setDrink = (drink) => {
if (drink) {
resultElement.innerText = `Enjoy your drink, ${drink}`;
} else {
resultElement.innerText = ``;
}
};
document.querySelector(".trigger").addEventListener("click", () => {
setDrink(null);
const coffee = window.confirm("Do you like coffee?");
if (coffee) return setDrink("Coffee");
const tea = window.confirm("Do you like tea?");
if (tea) return setDrink("Tea");
setDrink("Tequila");
});
</script>
</body>
Let's imagine we do it in (almost) the same way, but using React
:
const OrderDrink = () => {
const [drink, setDrink] = useState();
const modal = useModal();
return (
<div>
<button
onClick={async () => {
setDrink(null);
const coffee = await modal.open("Do you like coffee?");
if (coffee) return setDrink("Coffee");
const tea = await modal.open("Do you like tea?");
if (tea) return setDrink("Tea");
return setDrink("Tequila");
}}
>
I want to drink something!
</button>
<div>{drink && `${drink} is your drink!`}</div>
<ConfirmModal {...modal} />
</div>
);
}
Looks good, doesn't it. The magic happens inside the useModal
hook. Just create Promise
inside the open
function. And resolve it inside the close
function with the result (payload) of the user interaction.
const useModal = (initial = {}) => {
const state = useMemo(() => ({ resolve: null, data: initial.data }), []);
const [opened, setOpened] = useState(initial.opened);
const open = useCallback((data) => {
setOpened(true);
state.data = data;
return new Promise((resolve) => { state.resolve = resolve; });
}, []);
const close = useCallback((payload) => {
setOpened(false);
state.resolve(payload);
}, []);
return { opened, data: state.data, close, open };
};
And one more step - the modal dialog component:
const ConfirmModal = (props) => {
if (!props.opened) {
return null;
}
return (
<aside>
<div>
{props.data}
<br />
<button onClick={() => props.close(true)}>yes</button>
<button onClick={() => props.close(false)}>no</button>
</div>
</aside>
);
};
You can create a set of some reusable dialogs: AlertModal
, ConfirmModal
, PromptModal
. And depending on specific use cases, it could be a dialog with domain specific logic like EditProductModal
.
Source: https://gist.github.com/dsvgit/2f0a34201d1b075fdd42d2a79d5c5fc8
Sandbox: https://codesandbox.io/s/use-modal-hook-5nnr1v
Top comments (0)