reactjsnext.jsnextui

Using NextUI Pressable Card and Modals with React useState


I am using Next.js and the NextUI component library to create a pressable NextUI card, where upon clicking, will open up a NextUI modal. I have customized the card component in a myCard.js file. I have customized the desired modal component in a myModal.js file. I will be rendering the two in an index.js file where all of my other components get rendered. My current implementation has a pressable card, but when clicking on the card, the modal does not show up.

myCard.js:

const myCard = ({openModal}) =>
   *** setup code ***
   <Card 
      className={myCard}
      radius="lg"
      onPress={openModal}
      isPressable
   >
   *** more code ***

myModal.js:

const myModal = ({isModalOpen, closeModal}) => {
   *** setup code ***
   return (
      <Modal
         isOpen={isModalOpen}
         onClose={closeModal}
      >
         <ModalContent>
             {(onClose) => (
                <>
                  *** modal content ***
             }
         </ModalContent>
      </Modal>
   );

index.js

*** some other code ***

const [isModalOpen, setIsModalOpen] = useState(false);

  const openModal = () => {
    setIsModalOpen(true);
  };

  const closeModal = () => {
    setIsModalOpen(false);
  };

return (
  <ListCard openModal={openModal}/>
  <DetailCard 
      isOpen={isModalOpen}
      onClose={closeModal}
  />
*** other code ***
)

My best guess is that the since my components are in separate files, the state values are not being communicated properly. (If I replace myCard with just a button, it works as expected). What is the most optimal way to get the pressable Card to function properly as a button that opens up the modal?


Solution

  • So basically, you want to make a Card which when clicked shows a Modal !

    With respect to NextUI documentation you won't even need to use any states for handling Opening & Closing of Modal.

    const { isOpen, onOpen, onOpenChange } = useDisclosure();
    

    Look at the 1st Example : https://nextui.org/docs/components/modal#usage

    They have provided a hook, which handles all this states. You just have to set them on your Card component. Card has isPressable prop, you pass

    <Card onPress={onOpen} isPressable={true} >
    

    Below is a component I made :

    PressableCard.js Import it in a page & use.

    'use client'
    import React from 'react'
    import { Button, Card, CardBody, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, useDisclosure } from "@nextui-org/react";
    const PressableCard = () => {
    
        const { isOpen, onOpen, onOpenChange } = useDisclosure();
    
        console.log("isOpen : ", isOpen);
    
        return (
            <>
                <Card onPress={onOpen} isPressable={true} >
                    {/* PASS  onOpen TO onPress EVENT LISTENER*/}
                    <CardBody>
                        <p>CLICK THIS CARD !!</p>
                    </CardBody>
                </Card>
                <Modal isOpen={isOpen} onOpenChange={onOpenChange} isDismissable={false}>
                    {/* PASS isOpen STATE FROM  useDisclosure HOOK*/}
                    <ModalContent>
    
                        {(onClose) => (
                            <>
                                <ModalHeader >MODAL HEADER</ModalHeader>
                                <ModalBody>
                                    <p>
                                        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Expedita, officiis?
                                    </p>
    
                                </ModalBody>
                                <ModalFooter>
                                    <Button color="danger" variant="light"
                                        onPress={onClose}>
                                        {/* PASS  onClose FUNCTION TO onPress EVENT LISTENER*/}
                                        CLOSE
                                    </Button>
                                    <Button color="success" variant="light"
                                        onPress={onClose}>
                                        {/* PASS  onClose OR ANY OTHER FUNCTION TO onPress EVENT LISTENER*/}
                                        ACCEPT
                                    </Button>
                                </ModalFooter>
                            </>
                        )}
                    </ModalContent>
                </Modal>
            </>
        )
    }
    
    export default PressableCard
    

    Explaination :

    'use client' because these components cannot be rendered server-side as it uses events. Remove 'use client' & read the error.

    I have kept Card & Modal together, passed these states/values & functions const { isOpen, onOpen, onOpenChange } = useDisclosure();

    Card :

    Made Card pressable & passed a function on its onPress event:

    isPressable={true}
    onPress={onOpen}
    

    Modal :

    Modal is opened/closed based on

    isOpen={isOpen}
    

    so Modal's isOpen = isOpen from useDisclosure.

    Similarly, Modal's onOpenChange={onOpenChange} from useDisclosure.

    You may also read :

    If there's anything I left out, missed out, Please leave a comment. I will edit this answer.