diff --git a/packages/tiny-react-hooks/src/useDisclosure/useDisclosure.demo.tsx b/packages/tiny-react-hooks/src/useDisclosure/useDisclosure.demo.tsx
index dbde126..2a8ebb6 100644
--- a/packages/tiny-react-hooks/src/useDisclosure/useDisclosure.demo.tsx
+++ b/packages/tiny-react-hooks/src/useDisclosure/useDisclosure.demo.tsx
@@ -1,12 +1,24 @@
import { useDisclosure } from './useDisclosure';
-export default function Component() {
+export function Component() {
const {
isOpen,
onClose,
onOpen,
onToggle,
} = useDisclosure();
+ // const {
+ // isOpen,
+ // onClose,
+ // onOpen,
+ // onToggle,
+ // } = useDisclosure(false);
+ // const {
+ // isOpen,
+ // onClose,
+ // onOpen,
+ // onToggle,
+ // } = useDisclosure(() => false);
return (
<>
@@ -18,4 +30,4 @@ export default function Component() {
>
)
-}
\ No newline at end of file
+}
diff --git a/packages/tiny-react-hooks/src/useDisclosure/useDisclosure.test.ts b/packages/tiny-react-hooks/src/useDisclosure/useDisclosure.test.ts
index ab7a4ee..c1ef5d8 100644
--- a/packages/tiny-react-hooks/src/useDisclosure/useDisclosure.test.ts
+++ b/packages/tiny-react-hooks/src/useDisclosure/useDisclosure.test.ts
@@ -12,10 +12,16 @@ describe('useDisclosure()', () => {
expect(typeof result.current.onToggle).toBe('function');
});
+ describe('with no default value', () => {
+ it('should return isOpen with false when nothing is passed as argument', () => {
+ const { result } = renderHook(() => useDisclosure());
+ expect(result.current.isOpen).toBe(false);
+ });
+ });
describe('with default value', () => {
- describe('with correct default value as boolean', () => {
- describe('should work with default value', () => {
+ describe('with correct default value', () => {
+ describe('with default value is a boolean', () => {
it('should return isOpen with true', () => {
const { result } = renderHook(() => useDisclosure(true));
expect(result.current.isOpen).toBe(true);
@@ -24,20 +30,39 @@ describe('useDisclosure()', () => {
const { result } = renderHook(() => useDisclosure(false));
expect(result.current.isOpen).toBe(false);
});
- it('should return isOpen with false when nothing is passed as argument', () => {
- const { result } = renderHook(() => useDisclosure());
+ });
+
+ describe('with default value is function', () => {
+ it('should return isOpen with true', () => {
+ const { result } = renderHook(() => useDisclosure(() => true));
+ expect(result.current.isOpen).toBe(true);
+ });
+ it('should return isOpen with false', () => {
+ const { result } = renderHook(() => useDisclosure(() => false));
expect(result.current.isOpen).toBe(false);
});
});
});
describe('with incorrect default value type', () => {
- it('should throw an error', () => {
- const nonBoolean = '' as never;
- vi.spyOn(console, 'error').mockImplementation(() => vi.fn());
- expect(() => {
- renderHook(() => useDisclosure(nonBoolean));
- }).toThrowError('defaultValue must be a boolean value');
- vi.resetAllMocks();
+ describe('with default value is a boolean', () => {
+ it('should throw an error', () => {
+ const nonBoolean = '' as never;
+ vi.spyOn(console, 'error').mockImplementation(() => vi.fn());
+ expect(() => {
+ renderHook(() => useDisclosure(nonBoolean));
+ }).toThrowError('defaultValue must be a boolean value');
+ vi.resetAllMocks();
+ });
+ });
+ describe('with default value is a function', () => {
+ it('should throw an error', () => {
+ const nonBoolean = () => '' as never;
+ vi.spyOn(console, 'error').mockImplementation(() => vi.fn());
+ expect(() => {
+ renderHook(() => useDisclosure(nonBoolean));
+ }).toThrowError('defaultValue must be a boolean value');
+ vi.resetAllMocks();
+ });
});
});
});
diff --git a/packages/tiny-react-hooks/src/useDisclosure/useDisclosure.ts b/packages/tiny-react-hooks/src/useDisclosure/useDisclosure.ts
index 1ff7267..ca79a78 100644
--- a/packages/tiny-react-hooks/src/useDisclosure/useDisclosure.ts
+++ b/packages/tiny-react-hooks/src/useDisclosure/useDisclosure.ts
@@ -1,4 +1,4 @@
-import { useCallback, useState } from 'react';
+import { useCallback, useRef, useState } from 'react';
type UseDisclosureReturn = {
isOpen: boolean;
@@ -7,9 +7,11 @@ type UseDisclosureReturn = {
onToggle: () => void;
}
+type DisclosureDefaultValue = boolean | (() => boolean);
+
/**
* Custom hook that handles boolean state with useful utility functions.
- * @param {boolean} [defaultValue] - The initial value for the boolean state (default is `false`).
+ * @param {DisclosureDefaultValue} [defaultValue] - The initial value or produce default value function for the boolean state (default is `false`).
* @returns {UseDisclosureReturn} An object containing the boolean state value and utility functions to manipulate the state.
* @throws Will throw an error if `defaultValue` is an invalid boolean value.
* @public
@@ -18,9 +20,10 @@ type UseDisclosureReturn = {
* const { isOpen, onOpen, onClose, onToggle } = UseDisclosureReturn(true);
* ```
*/
-export function useDisclosure(defaultValue = false): UseDisclosureReturn {
- if (typeof defaultValue !== 'boolean') throw new Error('defaultValue must be a boolean value');
- const [isOpen, setOpen] = useState(defaultValue);
+export function useDisclosure(init: DisclosureDefaultValue = false): UseDisclosureReturn {
+ const initialValue = useRef(typeof init === 'function' ? init() : init);
+ if (typeof initialValue.current !== 'boolean') throw new Error('defaultValue must be a boolean value');
+ const [isOpen, setOpen] = useState(initialValue.current);
const onOpen = useCallback(() => {
setOpen(true);