Unable to close modal: setter function used to change variable state which closes modal never executes

24 views Asked by At

Right now I'm working on a menu page for a website on the client side using Next.js. When the menu for a restaurant renders, each list item represents an item on the menu. Each menu item has its own modal and an onClick prop that takes a handler which calls a setOpenModalId setter function to set openModalId variable state. This tells the browser which modal to display when the page rerenders.

I'm able to bring up a modal for each item using a handleItemClick handler that calls the setOpenModalId setter, but when I call the closeModal handler which should set the openModalId state to null, effectively closing the modal on rerender, the setOpenModalId setter never appears to execute at all. In the example below, the onClose handler should be invoked when clicking the div that represents the modal backdrop or when clicking the figure that wraps an "X" icon in the modal itself. When the "X" icon is clicked, the onClose handler is invoked and "Nothing happening" is logged to the browser console, but the setter never appears to execute.

I'm aware that useState setter calls are asynchronous and the state change isn't reflected immediately, and I have a useEffect hook with the openModalId state variable as a dependency set up to log the value of openModalId when it finally does change. The issue isn't that the state isn't changing immediately, it's that it doesn't appear to update at all, and I'm never able to close a modal once it's opened.

I've set up a context to share the state and a useModal custom hook for functionality that manages the state. The context provider is used in the layout.jsx page that wraps the child components where the custom hook and state are used.

I've spent longer than I'd like on this issue, and I'm not sure what I'm missing here. Sorry I can't be more specific. Any ideas would be appreciated.

Here's the relevant code:

// ModalContext.jsx
"use client";
import React, {createContext, useState, useEffect} from 'react';
const ModalContext = createContext(null);

const ModalProvider = props => {
  const [openModalId, setOpenModalId] = useState(null);

  useEffect(() => {
    console.log(openModalId + " (at ModalContext)");
  }, [openModalId]);

  return (
    <ModalContext.Provider 
      value={{openModalId, setOpenModalId}}
    >
      {props.children}
    </ModalContext.Provider>
  );
};

export {ModalContext, ModalProvider};
// useModal.jsx
"use client";
import React, {useContext} from 'react';
import { ModalContext } from '../contexts/ModalContext';
import getCopy from '../app/lib/utils/getCopy';

const useModal = () => {
  const {openModalId, setOpenModalId} = useContext(ModalContext);

  const handleItemClick = (item) => {
    setOpenModalId(item.id)
  };

  const closeModal = () => {
    console.log("Nothing Happening"); // Nothing Happening
    setOpenModalId(null);
  };

  return {
    closeModal, 
    handleItemClick,
    openModalId,
    setOpenModalId
  };
};

export default useModal;
// page.jsx
function FoodCategory({category, items}) {
  const {closeModal, openModalId, handleItemClick} = useModal();
  return (
    <li>
    <h3>{category}</h3>
    <section>
      <ul>
        {items.map((item) => {
          return (
            <li
              key={item.id}
              onClick={() => {handleItemClick(item)}}
            >
              {
                openModalId === item.id && (
                  <CartModal 
                    item={item} 
                    isOpen={true} 
                    onClose={closeModal} 
                    pathname={"Menu"} 
                  />
                )
              }

            
           // code omitted...


            </li>
          )
        })}
      </ul>
    </section>
  </li>
  )
}
// CartModal.jsx
import { GrClose } from "react-icons/gr";

function CartModal({item, isOpen, onClose, pathname}) {
  if (!isOpen) return null;

  // code omitted...

  return (
    <div onClick={onClose} className='modal'>
      <div className='modal-content'>
        <div>
          <figure 
            onClick={onClose} 
           >
            <GrClose />
          </figure>
        </div>

     // Code omitted...
   
     </div>
    </div>
  );
}
0

There are 0 answers