Skip to content

Commit

Permalink
✨ feat(useEventCallback): allow optional callback (#550)
Browse files Browse the repository at this point in the history
* feat(useEventCallback): allow optional callback

Updated the useEventCallback hook to accept an optional callback function. This improves developer experience by allowing more flexibility in the use of the hook.

* Create six-ties-fetch.md

---------

Co-authored-by: Julien <juliencaron@protonmail.com>
  • Loading branch information
Newbie012 and juliencrn authored Mar 21, 2024
1 parent 2e03935 commit 09341a3
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/six-ties-fetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"usehooks-ts": patch
---

feat(useEventCallback): allow optional callback (#550 by @Newbie012)
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { fireEvent, render, renderHook, screen } from '@testing-library/react'
import type { Mock } from 'vitest'

import { useEventCallback } from './useEventCallback'

Expand All @@ -22,4 +23,34 @@ describe('useEventCallback()', () => {

expect(fn).toHaveBeenCalled()
})

it('should be typed accordingly', () => {
const fn1: Mock<[React.MouseEvent<HTMLButtonElement>], void> = vi.fn()
const fn1Result = renderHook(() => useEventCallback(fn1))

expectTypeOf(fn1Result.result.current).toEqualTypeOf<
(event: React.MouseEvent<HTMLButtonElement>) => void
>()

const fn2 = undefined as
| Mock<[React.MouseEvent<HTMLButtonElement>], void>
| undefined
const fn2Result = renderHook(() => useEventCallback(fn2))

expectTypeOf(fn2Result.result.current).toEqualTypeOf<
((event: React.MouseEvent<HTMLButtonElement>) => void) | undefined
>()
})

it('should allow to pass optional callback without errors', () => {
const optionalFn = undefined as
| ((event: React.MouseEvent<HTMLButtonElement>) => void)
| undefined

const { result } = renderHook(() => useEventCallback(optionalFn))

render(<button onClick={result.current}>Click me</button>)

fireEvent.click(screen.getByText('Click me'))
})
})
12 changes: 10 additions & 2 deletions packages/usehooks-ts/src/useEventCallback/useEventCallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ import { useIsomorphicLayoutEffect } from '../useIsomorphicLayoutEffect'
*/
export function useEventCallback<Args extends unknown[], R>(
fn: (...args: Args) => R,
): (...args: Args) => R {
): (...args: Args) => R
export function useEventCallback<Args extends unknown[], R>(
fn: ((...args: Args) => R) | undefined,
): ((...args: Args) => R) | undefined
export function useEventCallback<Args extends unknown[], R>(
fn: ((...args: Args) => R) | undefined,
): ((...args: Args) => R) | undefined {
const ref = useRef<typeof fn>(() => {
throw new Error('Cannot call an event handler while rendering.')
})
Expand All @@ -28,5 +34,7 @@ export function useEventCallback<Args extends unknown[], R>(
ref.current = fn
}, [fn])

return useCallback((...args: Args) => ref.current(...args), [ref])
return useCallback((...args: Args) => ref.current?.(...args), [ref]) as (
...args: Args
) => R
}

0 comments on commit 09341a3

Please sign in to comment.