Skip to content

Commit d6ca420

Browse files
committed
feat(hooks): improve event handler hooks:
- refactor: create helper to reuse event tigger logic - feat(hooks): warn if no componentId nor global option for handlers - feat(hooks): obtain componentId from context on event handlers - feat(hooks): Add new (backward-compatible) method signature - chore: create tests for new cases
1 parent ec61e6d commit d6ca420

37 files changed

+1017
-158
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import extractComponentIdFromArgs from './extractComponentIdFromArgs'
2+
3+
describe('extractComponentIdFromArgs', () => {
4+
it('should return undefined ', () => {
5+
const componentId = extractComponentIdFromArgs(undefined)
6+
expect(componentId).toBeUndefined()
7+
})
8+
9+
it('should return componentId is string argument', () => {
10+
const componentId = extractComponentIdFromArgs('componentId')
11+
expect(componentId).toBe('componentId')
12+
})
13+
14+
it('should return componentId options provided', () => {
15+
const componentId = extractComponentIdFromArgs({ componentId: 'componentId' })
16+
expect(componentId).toBe('componentId')
17+
})
18+
})
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import isString from './isString'
2+
import { BaseEventHandlerOptions } from '../types/BaseEventHandlerOptions'
3+
4+
function extractComponentIdFromArgs<T extends BaseEventHandlerOptions>(
5+
componentIdOrOptions?: string | T
6+
): string | undefined {
7+
if (componentIdOrOptions === undefined) {
8+
return
9+
}
10+
11+
if (isString(componentIdOrOptions)) {
12+
return componentIdOrOptions
13+
}
14+
15+
return (componentIdOrOptions as T).componentId
16+
}
17+
18+
export default extractComponentIdFromArgs

src/helpers/isString.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import isString from './isString'
2+
3+
describe('isString', () => {
4+
it('should return false', () => {
5+
expect(isString()).toBeFalsy()
6+
expect(isString({})).toBeFalsy()
7+
expect(isString([])).toBeFalsy()
8+
expect(isString(false)).toBeFalsy()
9+
expect(isString(1)).toBeFalsy()
10+
})
11+
12+
it('should return true', () => {
13+
expect(isString('str')).toBeTruthy()
14+
})
15+
})

src/helpers/isString.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function isString(value?: any): value is string {
2+
return typeof value === 'string'
3+
}
4+
5+
export default isString
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { ComponentEvent } from 'react-native-navigation'
2+
3+
function triggerIfComponentIdMatches<T extends ComponentEvent>(
4+
handler: (event: T) => any,
5+
event: T,
6+
componentId?: string
7+
): void {
8+
if (componentId && event.componentId !== componentId) {
9+
return
10+
}
11+
12+
handler(event)
13+
}
14+
15+
export default triggerIfComponentIdMatches
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import warnIfMissingComponentId from './warnIfMissingComponentId'
2+
3+
describe('warnIfMissingComponentId', () => {
4+
beforeEach(() => {
5+
jest.spyOn(console, 'warn').mockReturnValue()
6+
})
7+
8+
afterEach(jest.clearAllMocks)
9+
10+
it('should warn if componentId not provided and global is false', () => {
11+
warnIfMissingComponentId('hook', undefined, false)
12+
13+
expect(console.warn).toBeCalledWith(
14+
'"hook" hook declared without providing "componentId" context. Make sure you are passing "componentId" as argument or wrapping your screen with "NavigationProvider" or "withNavigation" HOC'
15+
)
16+
})
17+
18+
it('should warn if componentId not nor global params provided', () => {
19+
warnIfMissingComponentId('hook')
20+
21+
expect(console.warn).toBeCalledWith(
22+
'"hook" hook declared without providing "componentId" context. Make sure you are passing "componentId" as argument or wrapping your screen with "NavigationProvider" or "withNavigation" HOC'
23+
)
24+
})
25+
26+
it('should not warn if global parameter provided provided ', () => {
27+
warnIfMissingComponentId('hook', undefined, true)
28+
29+
expect(console.warn).not.toBeCalled()
30+
})
31+
32+
it('should not warn componentId provided and global is true', () => {
33+
warnIfMissingComponentId('hook', 'componentId', true)
34+
35+
expect(console.warn).not.toBeCalled()
36+
})
37+
38+
it('should not warn componentId provided and global is false', () => {
39+
warnIfMissingComponentId('hook', 'componentId', false)
40+
41+
expect(console.warn).not.toBeCalled()
42+
})
43+
})
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
function warnIfMissingComponentId(hookName: string, componentId?: string, global?: boolean): void {
2+
if (global || componentId) {
3+
return
4+
}
5+
6+
console.warn(
7+
`"${hookName}" hook declared without providing "componentId" context. Make sure you are passing "componentId" as argument or wrapping your screen with "NavigationProvider" or "withNavigation" HOC`
8+
)
9+
}
10+
11+
export default warnIfMissingComponentId

src/hooks/useNavigation.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useMemo, useContext } from 'react'
2-
import createNavigationHelpers from '../helpers/createNavigationHelpers'
32
import { NavigationContext } from '../contexts'
3+
import createNavigationHelpers from '../helpers/createNavigationHelpers'
44

55
/**
66
* Returns a set of action helpers for
@@ -17,11 +17,10 @@ const useNavigation = (
1717
*/
1818
componentId?: string
1919
) => {
20-
const { componentId: componentIdFromContext } = useContext(NavigationContext)
21-
const id = componentId || componentIdFromContext
20+
const { componentId: id = componentId } = useContext(NavigationContext)
2221

2322
if (!id) {
24-
throw new Error('Missing componentId. Use NavigationContext or pass componentId as argument.')
23+
throw new Error('Missing "componentId". Use NavigationContext or pass "componentId" as argument.')
2524
}
2625

2726
return useMemo(() => createNavigationHelpers(id), [id])

src/hooks/useNavigationBottomTabLongPress.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ describe('useNavigationBottomTabLongPress', () => {
2020
})
2121
})
2222

23+
afterEach(jest.clearAllMocks)
24+
2325
it('should remove the event listener on unmount', () => {
2426
const { result, unmount } = renderHook(() => {
2527
useNavigationBottomTabLongPress(() => {})

src/hooks/useNavigationBottomTabLongPress.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ function useNavigationBottomTabLongPress(
99
/**
1010
* Function called each time the event is triggered.
1111
*/
12-
handler: (event: BottomTabLongPressedEvent) => void
13-
) {
12+
handler: (event: BottomTabLongPressedEvent) => any
13+
): void {
1414
useLayoutEffect(() => {
1515
const subscription = Navigation.events().registerBottomTabLongPressedListener(handler)
1616

0 commit comments

Comments
 (0)