I created a Modal
as compound component that manages all its state itself. Now I’m trying to open modal and render PostDetails
component inside it. Modal
getting open and component rendering as well but URL not getting updated.
If I go without modal, URL is updated, but I want to open postDetails component inside modal and at the same time URL should also update. I’ll use params to fetch post details later.
Modal component
import { createContext, useState, useContext, cloneElement } from "react";
import { createPortal } from "react-dom";
import { RiCloseLargeLine } from "react-icons/ri";
import PropTypes from 'prop-types'
const ModalContext = createContext();
const ModalProvider = ({ children }) => {
const [openName, setOpenName] = useState("");
const close = () => setOpenName("");
const open = (name) => setOpenName(name);
return (
<ModalContext.Provider value={{ openName, close, open }}>
{children}
</ModalContext.Provider>
);
};
const useModalContext = () => useContext(ModalContext);
const Modal = ({ children }) => {
return <>{children}</>;
};
const ModalOverlay = ({ children, isOpen, onClose }) => {
if (!isOpen) return null;
return <div className="fixed inset-0 z-50 flex items-center justify-center">
<div
className="fixed inset-0 backdrop-blur-[2px]"
onClick={onClose}
></div>
<div className="relative z-50 rounded-lg shadow-lg ">
{children}
</div>
<button
className="absolute md:top-5 top-3 right-3 md:right-5 p-1 md:p-1.5 "
onClick={onClose}
>
<RiCloseLargeLine className="w-7 h-7 " />
</button>
</div>
};
const Open = ({ children, opens: opensWindowName }) => {
const { open } = useModalContext();
return cloneElement(children, { onClick: () => open(opensWindowName) });
};
const Window = ({ children, name }) => {
const { openName, close } = useModalContext();
if (name !== openName) return null;
return createPortal(
<ModalOverlay isOpen={true} onClose={close}>
{cloneElement(children, { closeModal: close })}
</ModalOverlay>,
document.body
);
};
Modal.Open = Open;
Modal.Window = Window;
ModalContext.propTypes={
children: PropTypes.element
}
ModalProvider.propTypes={
children: PropTypes.element
}
Modal.propTypes={
children: PropTypes.element
}
ModalOverlay.propTypes={
children: PropTypes.element,
isOpen: PropTypes.bool,
onClose: PropTypes.func
}
Window.propTypes={
children: PropTypes.element,
name: PropTypes.string
}
export { ModalProvider, useModalContext, Modal };
Below is list of posts, and if I click any of them, URL should be updated and post should be open inside the modal. If user click anywhere except the post, modal will get close and user should got back to previous state ( post list page ).
import { useNavigate, useOutletContext } from "react-router-dom";
import { ModalProvider, Modal } from "../../ui/Modal";
import PostDetails from "./PostDetails";
const UserPosts = () => {
const [{ ownPosts }] = useOutletContext();
const navigate = useNavigate();
return (
<ModalProvider>
<ul className="grid gap-2 py-2 lg:grid-cols-3 xl:grid-cols-4 md:grid-cols-2 max-sm:gap-y-2">
{ownPosts &&
ownPosts?.map((post) => (
<figure
key={post?._id}
className=" rounded max-h-[340px] h-[340px]"
>
<Modal.Open opens="post-details-open">
<img
onClick={() => navigate(`/post/${post?._id}`)}
src={post?.mediaUrl}
alt="post image"
className="object-cover w-full h-full rounded"
/>
</Modal.Open>
</figure>
))}
</ul>
<Modal.Window name="post-details-open">
<PostDetails />
</Modal.Window>
</ModalProvider>
);
};
export default UserPosts;
I tried to go with Link
component as well but result is same as with navigate. How can I achieve it?