diff --git a/.changeset/early-ghosts-fix.md b/.changeset/early-ghosts-fix.md
new file mode 100644
index 0000000000..b0b21239d7
--- /dev/null
+++ b/.changeset/early-ghosts-fix.md
@@ -0,0 +1,6 @@
+---
+"@nextui-org/modal": patch
+"@nextui-org/use-disclosure": patch
+---
+
+Added useEffect in useModal to fire onOpenChange and in useDisclosure, onOpenChange now accepts onOpen as parameter(#3887)
diff --git a/packages/components/modal/__tests__/modal.test.tsx b/packages/components/modal/__tests__/modal.test.tsx
index b207a2269c..619570ea62 100644
--- a/packages/components/modal/__tests__/modal.test.tsx
+++ b/packages/components/modal/__tests__/modal.test.tsx
@@ -1,8 +1,9 @@
import * as React from "react";
import {render, fireEvent} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
+import {Button} from "@nextui-org/button";
-import {Modal, ModalContent, ModalBody, ModalHeader, ModalFooter} from "../src";
+import {Modal, ModalContent, ModalBody, ModalHeader, ModalFooter, useDisclosure} from "../src";
// e.g. console.error Warning: Function components cannot be given refs.
// Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
@@ -91,6 +92,38 @@ describe("Modal", () => {
expect(onClose).toHaveBeenCalled();
});
+ const ModalWrapper = ({onOpenChange}) => {
+ const {isOpen, onOpen} = useDisclosure();
+
+ return (
+ <>
+
+
+
+ Modal header
+ Modal body
+ Modal footer
+
+
+ >
+ );
+ };
+
+ test("should fire 'onOpenChange' callback when open button is clicked and modal opens", async () => {
+ const onOpenChange = jest.fn();
+
+ const {getByLabelText} = render();
+
+ const openButton = getByLabelText("Open");
+ const user = userEvent.setup();
+
+ await user.click(openButton);
+
+ expect(onOpenChange).toHaveBeenCalled();
+ });
+
it("should hide the modal when pressing the escape key", () => {
const onClose = jest.fn();
diff --git a/packages/components/modal/src/use-modal.ts b/packages/components/modal/src/use-modal.ts
index 66b6f7be63..931ce36076 100644
--- a/packages/components/modal/src/use-modal.ts
+++ b/packages/components/modal/src/use-modal.ts
@@ -3,7 +3,7 @@ import type {HTMLMotionProps} from "framer-motion";
import {AriaModalOverlayProps} from "@react-aria/overlays";
import {useAriaModalOverlay} from "@nextui-org/use-aria-modal-overlay";
-import {useCallback, useId, useRef, useState, useMemo, ReactNode} from "react";
+import {useCallback, useId, useRef, useState, useMemo, ReactNode, useEffect} from "react";
import {modal} from "@nextui-org/theme";
import {
HTMLNextUIProps,
@@ -129,6 +129,12 @@ export function useModal(originalProps: UseModalProps) {
},
});
+ useEffect(() => {
+ if (isOpen) {
+ onOpenChange?.(isOpen);
+ }
+ }, [isOpen]);
+
const {modalProps, underlayProps} = useAriaModalOverlay(
{
isDismissable,
diff --git a/packages/hooks/use-disclosure/src/index.ts b/packages/hooks/use-disclosure/src/index.ts
index c5bccc11df..91675e52e2 100644
--- a/packages/hooks/use-disclosure/src/index.ts
+++ b/packages/hooks/use-disclosure/src/index.ts
@@ -44,11 +44,14 @@ export function useDisclosure(props: UseDisclosureProps = {}) {
onOpenPropCallbackRef?.();
}, [isControlled, onOpenPropCallbackRef]);
- const onOpenChange = useCallback(() => {
- const action = isOpen ? onClose : onOpen;
+ const onOpenChange = useCallback(
+ (isOpen: boolean) => {
+ const action = isOpen ? onOpen : onClose;
- action();
- }, [isOpen, onOpen, onClose]);
+ action();
+ },
+ [isOpen, onOpen, onClose],
+ );
return {
isOpen: !!isOpen,