Home > Back-end >  How to handle multiple modals at different positions in React?
How to handle multiple modals at different positions in React?

Time:01-08

I am creating a portfolio site using Next Js.

I have a work section which consists of a screenshot of the projects. These are placed towards the left/right of the screen alternatively (refer screenshot attached).

I want to have a modal (explaining the project, links to code, tech stack used, etc) that pops up in the middle of the screenshot div (like in the pic attached) when a particular project's screenshot is clicked on.

But the problem is, Every other project's modal gets triggered too. I want only that particular modal to pop up.

Before Clicking on Screenshot: Before Clicking on Screenshot

After Clicking on Screenshot: After Clicking on Screenshot

I had faced a similar issue on another project when all modals in the page gets triggered, but I could fix it by passing modal content data using an arrow function as all modals were rendered in the center of the screen.
But for this, the Modals are positioned differently for each project. I'm not sure how to code them into place.

Here's my code:

WorkItemsDesk.js

export const WorkItemsDesk = () => {
  const [showModal, setShowModal] = useState(false);
  const togglePopUp = () => setShowModal(!showModal);

  return (
    <div className="flex flex-col my-[100px] gap-[100px] z-10 ">
      {projects.map((project) => (
        <div
          key={project.title}
          onClick={() => togglePopUp()} //This is so the modal closes when clicked outside
          className="w-[1000px] z-20 hover:cursor-pointer relative bg-no-repeat bg-center bg-fixed bg-clip-border bg-contain h-[470px] flex justify-center items-center odd:ml-auto hover:shadow-2xl transition-all duration-300 ease-in-out"
          style={{
            background: `linear-gradient(0deg, rgba(4, 4, 4, 0.65), rgba(4, 4, 4, 0.65)), url(${project.image});`,
            backgroundClip: "border-box",
            backgroundRepeat: "no-repeat",
            backgroundPosition: "center",
            backgroundSize: "cover",
          }}
        >
          <h1 className="font-montserrat-extrabold text-[40px] text-light-text">
            {project.title}
          </h1>
          <WorkModalDesk project={project} showModal={showModal} />
        </div>
      ))}
    </div>
  );
};

WorkModalDesk.js

export const WorkModalDesk = ({
  project: { p1, p2, tech, sourceCode, liveDemo, buttons },
  showModal,
}) => {
  return (
    <div
      className={
        showModal
          ? "flex pt-[24px] absolute px-[30px] pb-[34px] bg-dark rounded-lg flex-col gap-[40px] max-w-[450px] "
          : "hidden"
      }
    >
      <p className="text-[18px] text-light-text font-montserrat">
        {p1}
        <span className="block mt-[28px]">{p2}</span>
      </p>
      .
      .
      .
      .
    </div>
  );
};

CodePudding user response:

The reason all your modals are displayed at once is that you have a single state to manage all rendered modals.

I would suggest to move the loop outside of WorkItemDesk component as follows:

export const WorkItemsDesk = () => {
  const [showModal, setShowModal] = useState(false);
  const togglePopUp = () => setShowModal((prev) => !prev);

  return (
    <div className="flex flex-col my-[100px] gap-[100px] z-10 ">
      <div
          onClick={() => togglePopUp()} //This is so the modal closes when clicked outside
          className="w-[1000px] z-20 hover:cursor-pointer relative bg-no-repeat bg-center bg-fixed bg-clip-border bg-contain h-[470px] flex justify-center items-center odd:ml-auto hover:shadow-2xl transition-all duration-300 ease-in-out"
          style={{
            background: `linear-gradient(0deg, rgba(4, 4, 4, 0.65), rgba(4, 4, 4, 0.65)), url(${project.image});`,
            backgroundClip: "border-box",
            backgroundRepeat: "no-repeat",
            backgroundPosition: "center",
            backgroundSize: "cover",
          }}
        >
          <h1 className="font-montserrat-extrabold text-[40px] text-light-text">
            {project.title}
          </h1>
          <WorkModalDesk project={project} showModal={showModal} />
        </div>
    </div>
  );
};

Then in the parent component of WorkItemDesk add your projects loop as follows:

export const Parent = () => (
  <>
   {projects.map((project) => (
        <WorkItemDesk key={project.title} />
      ))}
  </>
)
  • Related