16

I am using Chakra UI and I want to use two modals in a single component.

const { isOpen , onOpen, onClose } = useDisclosure()

 <Button colorScheme="teal" size="xs" mr='2' onClick={onOpen} >
                        Edit
                    </Button>
                    {/* Edit modal */}
                    <Modal isOpen={isOpen} onClose={onClose}>
                            <ModalOverlay />
                            <ModalContent>
                            <ModalHeader>Edit Modal</ModalHeader>
                            <ModalCloseButton />
                            <ModalBody>
                               Edit Modal
                            </ModalBody>

                            <ModalFooter>
                            <Button variant="ghost" mr={3} onClick={onClose}>Cancel</Button>
                                <Button colorScheme="red" onClick={()=>{deleteAddress(address.id)}}>
                                    Delete
                                </Button>
                                
                            </ModalFooter>
                            </ModalContent>
                        </Modal>


                    {/* Delete Address */}

                    <Button colorScheme="red" size="xs"  onClick={onOpen}>
                        Delete
                    </Button> 
                    <Modal isOpen={isOpen} onClose={onClose}>
                            <ModalOverlay />
                            <ModalContent>
                            <ModalHeader>Delete Shipping Address</ModalHeader>
                            <ModalCloseButton />
                            <ModalBody>
                                Are you sure you want to delete the shipping address?
                            </ModalBody>

                            <ModalFooter>
                            <Button variant="ghost" mr={3} onClick={onClose}>Cancel</Button>
                                <Button colorScheme="red" onClick={()=>{deleteAddress(address.id)}}>
                                    Delete
                                </Button>
                                
                            </ModalFooter>
                            </ModalContent>
                        </Modal>

isOpen, onOpen and onClose can't be changed as variables since they are inbuilt Chakra functions.

Can someone suggest me a method to variate two of these modal (Chakra UI) operations?

Joundill
  • 6,828
  • 12
  • 36
  • 50
Pasan Madhushan
  • 205
  • 1
  • 4
  • 10

5 Answers5

29

You can simply change the name of variables isOpen, onOpen and onClose lets go:

const { isOpen: isEditOpen , onOpen: onEditOpen, onClose: onEditClose } = useDisclosure()
const { isOpen: isDeleteOpen , onOpen: onDeleteOpen, onClose: onDeleteClose } = useDisclosure()

{/* Edit Button*/}
<Button colorScheme="teal" size="xs" mr='2' onClick={onEditOpen} >
    Edit
</Button>

{/* Edit Modal */}
<Modal isOpen={isEditOpen} onClose={onEditClose}>
    <ModalOverlay />
    <ModalContent>
    <ModalHeader>Edit Modal</ModalHeader>
    <ModalCloseButton />
    <ModalBody>
        Edit Modal
    </ModalBody>
    <ModalFooter>
    <Button variant="ghost" mr={3} onClick={onEditClose}>Cancel</Button>
        <Button colorScheme="red" onClick={()=>{deleteAddress(address.id)}}>
            Delete
        </Button>
    </ModalFooter>
    </ModalContent>
</Modal>


{/* Delete Button*/}
<Button colorScheme="red" size="xs"  onClick={onDeleteOpen}>
    Delete
</Button> 

{/* Delete Modal*/}
<Modal isOpen={isDeleteOpen} onClose={onSecondModalClose}>
    <ModalOverlay />
    <ModalContent>
    <ModalHeader>Delete Shipping Address</ModalHeader>
    <ModalCloseButton />
    <ModalBody>
        Are you sure you want to delete the shipping address?
    </ModalBody>
    <ModalFooter>
    <Button variant="ghost" mr={3} onClick={onDeleteClose}>Cancel</Button>
        <Button colorScheme="red" onClick={()=>{deleteAddress(address.id)}}>
            Delete
        </Button>
    </ModalFooter>
    </ModalContent>
</Modal>

One more example:

First modal:

const { isOpen: isFirstModalOpen , onOpen: onFirstModalOpen, onClose: onFirstModalClose } = useDisclosure()

Second modal:

const { isOpen: isSecondModalOpen , onOpen: onSecondModalOpen, onClose: onSecondModalClose } = useDisclosure()

Now these variables have different values so you can use new names every where!

Prem G
  • 162
  • 3
  • 8
Alireza Khanamani
  • 311
  • 1
  • 3
  • 5
  • 1
    this made a lot more sense to me since I wanted a different component to control presentation, appreciate it! – fjlksahfob Aug 09 '21 at 19:56
14

You could create a custom modal component that uses useDisclosure. Then you can have multiple instances of this custom modal component without the modals sharing the same state:

const CustomModal = ({ showModalButtonText, modalHeader, modalBody }) => {
  const { isOpen, onOpen, onClose } = useDisclosure();
  return (
    <>
      <Button colorScheme="red" size="xs" onClick={onOpen}>
        {showModalButtonText}
      </Button>
      <Modal isOpen={isOpen} onClose={onClose}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>{modalHeader}</ModalHeader>
          <ModalCloseButton />
          <ModalBody>{modalBody}</ModalBody>

          <ModalFooter>
            <Button variant="ghost" mr={3} onClick={onClose}>
              Cancel
            </Button>
            <Button
              colorScheme="red"
              onClick={() => {
                alert(1);
              }}
            >
              Delete
            </Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
    </>
  );
};

export default function App() {
  return (
    <div className="App">
      <CustomModal
        showModalButtonText="Edit"
        modalHeader="Edit Modal"
        modalBody="Edit Modal"
      />
      <CustomModal
        showModalButtonText="Delete"
        modalHeader="Delete Shipping Address"
        modalBody="Are you sure you want to delete the shipping address?"
      />
    </div>
  );
}

This way each instance of CustomModal keeps track of its own isOpen, onOpen and onClose state.

Since in your question the only dynamic parts were the button for opening, the modal, the header text and the modal body text, I've made props so these can be set separately for each instance. If more parts need to be dynamic you can add props as needed.

I've changed some small things to the modal to make it easier to test.

5eb
  • 14,798
  • 5
  • 21
  • 65
  • 2
    Answer from Alireza provides much more flexibility... – Jan Z Nov 14 '21 at 07:38
  • 2
    no it doesn't.This is much better answer – Fo Nko Apr 23 '22 at 17:27
  • 1
    @FoNko no, Alireza's is much more flexible. If the modal state is stored in CustomModal, how do you tell it to open from a parent component? It's not possible with this solution. `useDisclosure` state should be _passed in_ to the modal component so it can be controlled from outside. – Nathan Jan 06 '23 at 04:46
  • 1
    If you really need to control the modals from the parent go with Alireza's approach. Downside to moving the state up like that though is that you cause stuff to rerender that doesn't need to rerender. If the state of one modal changes both modals rerender. So if you don't need to control the modals like this I would still go with the approach I've written here. You might be able to use memoization to fix the render problem (not sure, haven't tried that out). But in general I think you should use small and isolated components that manage their own state, that's the whole idea behind React. – 5eb Feb 05 '23 at 14:14
  • This is actually a better approach – Hassan Sani May 13 '23 at 15:03
3

you can simply create two instances of useDisclosure hook.

  const {
    isOpen: isAssociationModalOpen,
    onOpen: onOpenAssociationModal,
    onClose: onCloseAssociationModal,
  } = useDisclosure();
  const {
    isOpen: isRevocationModalOpen,
    onOpen: onOpenRevocationModal,
    onClose: onCloseRevocationModal,
  } = useDisclosure();
Ion Moraru
  • 71
  • 3
1

So basically just You can change the name of variables isOpen, onOpen and onClose or you can have two instances of useDisclosure that way you can use two or more modals in your code:

import React, { useState } from 'react'
import { data } from '../../../utils/Data/data'
import {
    Table,
    Thead,
    Tbody,
    Tfoot,
    Tr,
    Th,
    Td,
    Checkbox,
    CheckboxGroup,
    Skeleton,
    Box,
    VStack,
    Button,
    HStack,
    useDisclosure


} from '@chakra-ui/react'
import EditSiteModal from '../Modal/EditSiteModal'
import DeleteSiteModal from '../Modal/DeleteSiteModal'

const MySites = () => {
    const { isOpen: isEditOpen, onOpen: onEditOpen, onClose: onEditClose } = useDisclosure()
    const { isOpen: isDeleteOpen, onOpen: onDeleteOpen, onClose: onDeleteClose } = useDisclosure()


    return (
        <>
            <Box bgColor="white" p={10}>
                <Box bgColor="white">
                    <EditSiteModal
                        isOpen={isEditOpen}
                        onClose={onEditClose}
                    />

                    <DeleteSiteModal
                        isOpen={isDeleteOpen}
                        onClose={onDeleteClose}
                    />

                    <Table variant='simple'>
                        <Thead>
                            <Tr bg="#F9F9F9" p="2" >
                                <Th></Th>
                                <Th textTransform={'capitalize'}>Site Name</Th>
                                <Th textTransform={'capitalize'}>Admin</Th>
                                <Th textTransform={'capitalize'}>Creation date & Time</Th>
                                <Th textTransform={'capitalize'}>Actions</Th>
                            </Tr>
                        </Thead>

                        <Tbody>

                            {data &&
                                data.map((sites, index: number) => {

                                    return (
                                        <Tr key={index} cursor={"pointer"}>
                                            <Td><Checkbox></Checkbox></Td>
                                            <Td>{sites.site_name}</Td>
                                            <Td>{sites.admin}</Td>
                                            <Td>{sites.creation_date}</Td>
                                            <Td>
                                                <HStack>
                                                    <Button
                                                        type="button"
                                                        bg="transparent"
                                                        border="1px"
                                                        borderRadius="base"
                                                        borderColor="#1818183D"
                                                        width="inherit"
                                                        onClick={onDeleteOpen}
                                                        mr="8px"
                                                    >
                                                        Delete
                                                    </Button>
                                                    <Button
                                                        type="button"
                                                        bg="transparent"
                                                        border="1px"
                                                        borderRadius="base"
                                                        borderColor="#F6B319"
                                                        width="inherit"
                                                        onClick={onEditOpen}
                                                    >
                                                        Edit
                                                    </Button>
                                                </HStack>
                                            </Td>
                                        </Tr>
                                    );
                                })}
                        </Tbody>
                    </Table>
                </Box>
            </Box>
        </>
    )
}

export default MySites
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 17 '22 at 06:33
1

The other answers do not cover the case where you need to map through many elements and have each one open a modal with their values when they are clicked.

Here is a way to do it:

  • allow all those elements to open the same one modal
  • change that modal's contents with useState

How:

  • in each element's onClick, call a function that: sets a state with their value(s) + calls useDisclosure's onOpen()
  • simply use the state value(s) in the modal

You can see an example by searching the word "modal" on this page (the code section that includes const [modalValue, setModalValue] = useState({})): https://www.educative.io/answers/how-to-make-a-chakra-ui-and-react-to-do-list-app-using-hooks-only

Kitty
  • 89
  • 10