Using NextUI Pressable Card and Modals with React useState

1.4k views Asked by At

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?

1

There are 1 answers

0
Beast80K On BEST ANSWER

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} >
  • Yes you should keep the component that opens Modal together with modal or else you have to pass props this makes it unnecessarily difficult.
  • Instead keep them together, & handle opening closing using useDisclosure() hook.

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.

  • To get more idea about useDisclosure(), select useDisclosure() in your code editor & press F12 it will show you what functions & values it returns.
  • I have also console logged isOpen state/value, so when you click on card you can see it change in console.

You may also read :

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