Best Way to Add Popup Modals in React

Best Way to Add Popup Modals in React

Colby Fayock

2 недели назад

2,643 Просмотров

Ссылки и html тэги не поддерживаются


Комментарии:

@John-eq5cd
@John-eq5cd - 29.05.2024 11:43

Excellent. Thank you, Colby.

Ответить
@antoniocestari5775
@antoniocestari5775 - 24.05.2024 19:45

Hey Colby, thanks for your vids. It's always great to see you going through the code, you communicate things beautifully.

This video was very interesting to me because I'm actually looking for alternatives for SSR dialogs and I was considering the dialog element, but needed to explore it more. Some additional thoughts on what you came up with on this one:

I've noticed that the scroll goes all the way to the top when we set the body's overflow to hidden, which doesn't feel great IMO. There's a hack we can do to save the scroll position, make the body position fixed, and keep the thing we were looking at on the vp. I'd need to test what happens on resize tho.

The other thing I'd recommend (not UX-related) is to avoid using useEffect to show the modal. This pattern of event -> change state -> useEffect listens to the state change has been communicated as a bad one and we should avoid using useEffects for that kind of flow. Here we could create a function, simillar to the closeDialog one (openDialog for example) and just trigger the dialog method onClick instead of relying on a useEffect for it. We'd need the useEffect only to attach the listeners to the document body and at that point, as it would have only one responsibility, we could even abstract the addition to event listeners in a custom hook, resulting in no useEffect at all. (Not a useEffect hater, just trying to simplify stuff a bit)

Hope to see more of your content soon man, thanks!

Ответить
@mohamedelmhassani5618
@mohamedelmhassani5618 - 24.05.2024 18:45

Thaaaanks Colby, so helpful. no more extra KBs to my projects 😂

Ответить
@EverydayBeing-de1qu
@EverydayBeing-de1qu - 24.05.2024 12:53

Seems like alot of effort for a modal... I usually just create new component modal with fixed inset-0 bg-black bg-opacity-0 and then another div with h-full flex justify-center items-center and then a main with the actual modal and specify the h and w you want it to be.

Ответить
@dannievinther
@dannievinther - 24.05.2024 10:59

Awesome stuff :) A better choice, I believe, would be to use the Popover API: e.g., <dialog popover>. That way you get more of the functionality for free as well as a implicit role.

Ответить
@nikitaliakhovka3489
@nikitaliakhovka3489 - 24.05.2024 06:59

For the document overflow, you can leverage the power of the :has() selector:

CSS:
html:has(dialog[open]) { overflow: hidden; }

Tailwind:
(on html tag)
className="[&:has(dialog[open])]:overflow-hidden"

Ответить
@user-vd3ph6zh8q
@user-vd3ph6zh8q - 24.05.2024 05:37

Wouldn't you be able to do it without useEffect by composing event listeners like this?

"use client";

import React, { useRef, useState } from "react";
import Image from "next/image";

import Container from "@/components/Container";

import images from "@/data/images.json";
import { X } from "lucide-react";

interface Image {
alt: string;
height: number;
path: string;
width: number;
}

export default function Home() {
const dialogRef = useRef<HTMLDialogElement | null>(null);
const [activeIdx, setActiveIdx] = useState<number | undefined>(undefined);
const activeImg = activeIdx === undefined ? undefined : images[activeIdx];

function handleSelectImage(event: React.MouseEvent<HTMLButtonElement>) {
setActiveIdx(parseInt(event.currentTarget.value));
dialogRef.current?.showModal();
document.body.style.overflow = "hidden";
document.body.addEventListener("keydown", handleOnKeyDown);
dialogRef.current?.addEventListener(
"close",
() => {
document.body.style.overflow = "";
document.body.removeEventListener("keydown", handleOnKeyDown);
},
{ once: true }
);
}

function handleOnKeyDown(event: KeyboardEvent) {
setActiveIdx((prev) => {
if (prev === undefined) return;
switch (event.code) {
case "ArrowRight":
if (prev === images.length - 1) return 0;
return prev + 1;
case "ArrowLeft":
if (prev === 0) return images.length - 1;
return prev - 1;
default:
return prev;
}
});
}
return (
<Container className="mt-12">
<h1 className="text-4xl font-black mb-12">Game Night!</h1>
<ul className="grid grid-cols-2 md:grid-cols-3 gap-4">
{images.map((image, idx) => (
<li key={image.path}>
<button value={idx} onClick={handleSelectImage}>
<Image
className="block object-cover aspect-square"
src={image.path}
width={500}
height={500}
alt={image.alt}
sizes="33vw"
/>
</button>
</li>
))}
</ul>
{activeImg && (
<dialog
ref={dialogRef}
className="relative overflow-visible backdrop:bg-black/85 bg-transparent"
>
<div
className="max-h-[90vh] max-w-[90vw] rounded-sm overflow-hidden bg-black"
style={{
aspectRatio: `${activeImg.width} / ${activeImg.height}`,
}}
>
<Image
width={activeImg.width}
height={activeImg.height}
src={activeImg.path}
alt=""
unoptimized
/>
</div>
<button
className="absolute -top-2 -right-2 z-1 flex items-center justify-center w-5 h-5 bg-zinc-200 rounded-full shadow"
onClick={() => dialogRef.current?.close()}
>
<X className="w-4 h-4 text-zinc-900" />
<span className="sr-only">Close</span>
</button>
</dialog>
)}
</Container>
);
}

Ответить
@FlintBits
@FlintBits - 24.05.2024 03:08

Why wouldn't you use popover api? By definition it is the "best way"

Ответить
@AhmadJS
@AhmadJS - 23.05.2024 16:48

👍👍👍👍👍

Ответить