React modal with prop not triggered

  javascript, modal-dialog, reactjs

In a page in my react project I have a function that triggers a useState, which holds a boolean responsible for showing or not a modal that overlaps the same page.

  const [modalOpened, setModalOpened] = useState(false);

And this is the function responsible for handling the state change:

  const handleImageClick = () => {
    setModalOpened(!modalOpened);
  };

My modal accepts a prop that will be used to set the state that handles if the modal is displayed or not.

  <ProductModal openModal={modalOpened} />

And inside the productModal component, this is what happens:

  const [open, setOpen] = useState(openModal);

For some reason upon triggering the function that changes the state in the external page, the modal doesn’t show up. I may be doing something wrong regarding the rendering, but if you have a good solution, I’m listening.

Full code for the external page:

import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { WhiteLink } from "../shared/Buttons";
import { ProductAddProgressBar, Step } from "../shared/ProgressBars";
import { apiClient } from "../../API/ApiClient";
import { TemplateProductPTO } from "./AddProductPageSecond";
import { useAppDispatch } from "../../store/store";
import {
  selectSearchQuery,
  selectSearchResults,
  updateTemplateProduct,
  updateSearchQuery,
  updateSearchResults,
} from "../../store/inventory/slice";
import { useSelector } from "react-redux";
import ProductModal from "./ProductModal";

export const AddProductPageFirst = () => {
  const [modalOpened, setModalOpened] = useState(false);
  const dispatch = useAppDispatch();
  const searchQuery = useSelector(selectSearchQuery);
  const searchResults = useSelector(selectSearchResults);

  const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    dispatch(updateSearchQuery(event.target.value));
  };

  const searchProducts = async () => {
    if (searchQuery) {
      const results = await apiClient.searchProducts(searchQuery);
      dispatch(updateSearchResults(results));
    }
  };

  const enterSearchProducts = (event: any) => {
    var code = event.keyCode || event.which;
    if (code === 13) {
      //13 is the enter keycode
      searchProducts();
    }
  };

  const handleImageClick = () => {
    setModalOpened(!modalOpened);
    console.log(modalOpened);
  };

  const steps: Step[] = [
    {
      status: "current",
      name: "Select product",
      link: "/addproduct/1/",
    },
    {
      status: "upcoming",
      name: "Edit Details",
      link: "/addproduct/2/",
    },
    {
      status: "upcoming",
      name: "Add photos",
      link: "/addproduct/3/",
    },
    {
      status: "upcoming",
      name: "Summary",
      link: "/addproduct/4/",
    },
  ];

  useEffect(() => {}, [modalOpened]);

  return (
    <div className="container mx-auto px-4 sm:px-6 lg:px-8 my-32">
      <div className="mb-8">
        <WhiteLink message={"Exit"} link={"/inventory"} />
      </div>
      <ProductAddProgressBar steps={steps} />
      <div className="flex">
        <nav className="space-y-1 w-1/5 mr-8 mt-16" aria-label="Sidebar">
          {/*<!-- Current: "bg-gray-100 text-gray-900", Default: "text-gray-600 hover:bg-gray-50 hover:text-gray-900" -->*/}
          <a
            href="#"
            className="bg-gray-100 text-gray-900 group flex items-center px-3 py-2 text-sm font-medium rounded-md"
            aria-current="page"
          >
            <span className="truncate">Dashboard</span>

            {/*<!-- Current: "bg-white", Default: "bg-gray-100 text-gray-600 group-hover:bg-gray-200" -->*/}
            <span className="bg-white ml-auto inline-block py-0.5 px-3 text-xs rounded-full">
              5
            </span>
          </a>

          <a
            href="#"
            className="text-gray-600 hover:bg-gray-50 hover:text-gray-900 group flex items-center px-3 py-2 text-sm font-medium rounded-md"
          >
            <span className="truncate">Team</span>
          </a>

          <a
            href="#"
            className="text-gray-600 hover:bg-gray-50 hover:text-gray-900 group flex items-center px-3 py-2 text-sm font-medium rounded-md"
          >
            <span className="truncate">Projects</span>

            <span className="bg-gray-100 text-gray-600 group-hover:bg-gray-200 ml-auto inline-block py-0.5 px-3 text-xs rounded-full">
              19
            </span>
          </a>

          <a
            href="#"
            className="text-gray-600 hover:bg-gray-50 hover:text-gray-900 group flex items-center px-3 py-2 text-sm font-medium rounded-md"
          >
            <span className="truncate">Calendar</span>

            <span className="bg-gray-100 text-gray-600 group-hover:bg-gray-200 ml-auto inline-block py-0.5 px-3 text-xs rounded-full">
              20+
            </span>
          </a>

          <a
            href="#"
            className="text-gray-600 hover:bg-gray-50 hover:text-gray-900 group flex items-center px-3 py-2 text-sm font-medium rounded-md"
          >
            <span className="truncate">Documents</span>
          </a>

          <a
            href="#"
            className="text-gray-600 hover:bg-gray-50 hover:text-gray-900 group flex items-center px-3 py-2 text-sm font-medium rounded-md"
          >
            <span className="truncate">Reports</span>
          </a>
        </nav>
        <div className="w-4/5 h-full mt-16">
          <div className="mt-6 grid grid-cols-4 gap-6">
            <div className="col-span-4 sm:col-span-1" />

            <div className="col-span-4 sm:col-span-2 bg-white overflow-hidden shadow rounded-lg h-full">
              <div className="px-4 py-5 sm:p-6">
                <h2 className="text-center text-3xl font-extrabold tracking-tight text-gray-900 flex justify-center">
                  Find your product in the LocalShops database
                </h2>
              </div>
              <div className="px-4 py-5 sm:p-6">
                <div className="mt-1 flex rounded-md shadow-sm">
                  <div className="relative flex items-stretch flex-grow focus-within:z-10">
                    <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
                      {/* Heroicon name: solid/users */}
                      <svg
                        className="flex-shrink-0 h-5 w-5 text-gray-400"
                        xmlns="http://www.w3.org/2000/svg"
                        viewBox="0 0 20 20"
                        fill="currentColor"
                        aria-hidden="true"
                      >
                        <path
                          fillRule="evenodd"
                          d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
                          clipRule="evenodd"
                        />
                      </svg>
                    </div>
                  </div>
                  <input
                    onChange={(event) => handleSearchChange(event)}
                    onSubmit={() => searchProducts()}
                    onKeyPress={(event) => enterSearchProducts(event)}
                    value={searchQuery ?? ""}
                    type="text"
                    name="q"
                    id="q"
                    className="focus:ring-indigo-500 focus:border-indigo-500 block w-full rounded-none rounded-l-md pl-10 sm:text-sm border-gray-300"
                    placeholder="Type the name of your product"
                  />
                  <button
                    onClick={() => searchProducts()}
                    type="submit"
                    className="-ml-px relative inline-flex items-center space-x-2 py-2 px-4 border border-transparent rounded-r-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
                  >
                    {/* Heroicon name: solid/sort-ascending */}
                    <span>Search</span>
                  </button>
                </div>
              </div>
            </div>

            <div className="col-span-4 sm:col-span-1 bg-white overflow-hidden shadow rounded-lg h-full">
              <div className="px-4 py-5 sm:p-6">
                <h2 className="text-lg leading-6 font-medium text-gray-900 flex justify-center">
                  What is the LocalShops database?
                </h2>
                <p className="text-sm leading-5 font-normal text-gray-500">
                  All the products sold on LocalShops have to be included in the
                  database for them to be found when users search. By adding a
                  product from the database to your shop, you are able to add
                  the inventory and sell it.
                </p>
              </div>
            </div>
          </div>
          {/*  Table */}

          {searchResults.length > 0 && (
            <div className="flex flex-col mt-16">
              <div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
                <div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
                  <div className="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
                    <table className="min-w-full divide-y divide-gray-200">
                      <thead className="bg-gray-50">
                        <tr>
                          <th
                            scope="col"
                            className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
                          >
                            Image
                          </th>
                          <th
                            scope="col"
                            className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
                          >
                            Name
                          </th>
                          <th
                            scope="col"
                            className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
                          >
                            Product SKU
                          </th>
                          <th
                            scope="col"
                            className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
                          >
                            Rating
                          </th>
                          <th
                            scope="col"
                            className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
                          >
                            Stocked By
                          </th>
                          <th scope="col" className="relative px-6 py-3" />
                        </tr>
                      </thead>
                      <tbody className="bg-white divide-y divide-gray-200">
                        {searchResults.map(
                          (default_product: TemplateProductPTO) => (
                            <tr>
                              <td className="px-6 py-4 whitespace-nowrap">
                                <div
                                  className="flex items-center"
                                  onClick={() => handleImageClick()}
                                >
                                  <div className="flex-shrink-0 h-10 w-10">
                                    {default_product.photos.length > 0 && (
                                      <img
                                        className="h-10 w-10 rounded-sm"
                                        src={default_product.photos[0].image}
                                        alt=""
                                      />
                                    )}
                                  </div>
                                </div>
                              </td>
                              <td className="px-6 py-4 whitespace-nowrap">
                                <div className="flex items-center">
                                  <div>
                                    <div className="text-sm font-medium text-gray-900">
                                      {default_product.name}
                                    </div>
                                    <div className="text-sm text-gray-500 truncate max-w-xs">
                                      {default_product.desc}
                                    </div>
                                  </div>
                                </div>
                              </td>
                              <td className="px-6 py-4 whitespace-nowrap">
                                <div className="flex items-center">
                                  <div>
                                    <div className="text-sm font-medium text-gray-900">
                                      {default_product.sku}
                                    </div>
                                  </div>
                                </div>
                              </td>
                              {/*<td className="px-6 py-4 whitespace-nowrap">*/}
                              {/*  <span className="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">*/}
                              {/*    Active*/}
                              {/*  </span>*/}
                              {/*</td>*/}
                              <td className="px-6 py-4 whitespace-nowrap">
                                <div className="flex items-center">
                                  <div>
                                    <div className="text-sm font-medium text-gray-900">
                                      {default_product.rating}
                                    </div>
                                  </div>
                                </div>
                              </td>
                              <td className="px-6 py-4 whitespace-nowrap">
                                <div className="flex items-center">
                                  <div>
                                    <div className="text-sm font-medium text-gray-900">
                                      {`${
                                        default_product.products.length
                                      } shop${
                                        default_product.products.length > 1
                                          ? "s"
                                          : ""
                                      }`}
                                    </div>
                                  </div>
                                </div>
                              </td>
                              <td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
                                <Link
                                  to={{
                                    pathname: "/addproduct/2",
                                  }}
                                  onClick={() =>
                                    dispatch(
                                      updateTemplateProduct(default_product)
                                    )
                                  }
                                  className="font-medium text-indigo-600 hover:text-indigo-500"
                                >
                                  Choose
                                </Link>
                              </td>
                            </tr>
                          )
                        )}
                      </tbody>
                    </table>
                  </div>
                </div>
              </div>
            </div>
          )}
          <ProductModal openModal={modalOpened} />
        </div>
      </div>
    </div>
  );
};

Full code for Modal:

/* This example requires Tailwind CSS v2.0+ */
import { Fragment, useEffect, useState } from "react";
import { Dialog, Transition } from "@headlessui/react";
import { CheckIcon } from "@heroicons/react/outline";

interface modalProps {
  openModal: boolean;
}

const ProductModal: React.FC<modalProps> = ({ openModal }) => {
  const [open, setOpen] = useState(false);

  useEffect(() => {
    setOpen(openModal);
  }, [openModal]);

  return (
    <Transition.Root show={open} as={Fragment}>
      <Dialog
        as="div"
        static
        className="fixed z-10 inset-0 overflow-y-auto"
        open={open}
        onClose={() => setOpen(false)}
      >
        <div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
          <Transition.Child
            as={Fragment}
            enter="ease-out duration-300"
            enterFrom="opacity-0"
            enterTo="opacity-100"
            leave="ease-in duration-200"
            leaveFrom="opacity-100"
            leaveTo="opacity-0"
          >
            <Dialog.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
          </Transition.Child>

          {/* This element is to trick the browser into centering the modal contents. */}
          <span
            className="hidden sm:inline-block sm:align-middle sm:h-screen"
            aria-hidden="true"
          >
            ​
          </span>
          <Transition.Child
            as={Fragment}
            enter="ease-out duration-300"
            enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
            enterTo="opacity-100 translate-y-0 sm:scale-100"
            leave="ease-in duration-200"
            leaveFrom="opacity-100 translate-y-0 sm:scale-100"
            leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
          >
            <div className="inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full sm:p-6">
              <div>
                <div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100">
                  <CheckIcon
                    className="h-6 w-6 text-green-600"
                    aria-hidden="true"
                  />
                </div>
                <div className="mt-3 text-center sm:mt-5">
                  <Dialog.Title
                    as="h3"
                    className="text-lg leading-6 font-medium text-gray-900"
                  >
                    Payment successful
                  </Dialog.Title>
                  <div className="mt-2">
                    <p className="text-sm text-gray-500">
                      Lorem ipsum dolor sit amet consectetur adipisicing elit.
                      Consequatur amet labore.
                    </p>
                  </div>
                </div>
              </div>
              <div className="mt-5 sm:mt-6">
                <button
                  type="button"
                  className="inline-flex justify-center w-full rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:text-sm"
                  onClick={() => setOpen(false)}
                >
                  Go back to dashboard
                </button>
              </div>
            </div>
          </Transition.Child>
        </div>
      </Dialog>
    </Transition.Root>
  );
};

export default ProductModal;

Source: Ask Javascript Questions

LEAVE A COMMENT