Skip to content

Commit db207c0

Browse files
committed
RCE-17: fixed invalid state in hook
1 parent f3a90e8 commit db207c0

File tree

3 files changed

+70
-3
lines changed

3 files changed

+70
-3
lines changed

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ useCustomEventListener('my-event', (data) => {
4949
})
5050
```
5151

52+
If using state in listener handler, it's recommended to provide dependency list (same as second parameter of `useEffect`)
53+
54+
```javascript
55+
useCustomEventListener(
56+
'my-event',
57+
(data) => {
58+
doSomethingWithDataAndStates(data, state1, state2)
59+
},
60+
[state1, state2]
61+
)
62+
```
63+
5264
No need to remove event listener, it uses react's useEffect hook to remove listener on component unmount
5365

5466
And no need to worry about where component is present in dom, these events can be sent and listened to anywhere

__tests__/Test.test.tsx

+53
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,56 @@ describe('Test Component', () => {
7979
expect(data.textContent).toBe('data from hello')
8080
})
8181
})
82+
83+
describe('useCustomEventListener', () => {
84+
interface TestComponentProps {
85+
evenFired: (text: string) => void
86+
}
87+
88+
const TestComponent = ({ evenFired }: TestComponentProps) => {
89+
const [text, setText] = useState('initial')
90+
91+
useCustomEventListener(
92+
'eventFired',
93+
() => {
94+
evenFired(text)
95+
},
96+
[text]
97+
)
98+
99+
const handleStateChange = () => setText('changed')
100+
101+
const handleEventFire = () => emitCustomEvent('eventFired')
102+
103+
return (
104+
<>
105+
<button data-testid="state-changer" onClick={handleStateChange}>
106+
Change State
107+
</button>
108+
<button data-testid="fire-event-btn" onClick={handleEventFire}>
109+
Fire Event with data
110+
</button>
111+
</>
112+
)
113+
}
114+
test('its callback having latest state', (done) => {
115+
render(
116+
<TestComponent
117+
evenFired={(text: string) => {
118+
try {
119+
expect(text).toBe('changed')
120+
done()
121+
} catch (cause) {
122+
done(cause)
123+
}
124+
}}
125+
/>
126+
)
127+
128+
const stateChangerBtn = screen.getByTestId('state-changer')
129+
userEvent.click(stateChangerBtn)
130+
131+
const fireBtn = screen.getByTestId('fire-event-btn')
132+
userEvent.click(fireBtn)
133+
})
134+
})

src/index.tsx

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect } from 'react'
1+
import { DependencyList, useEffect } from 'react'
22

33
let targetElement: undefined | HTMLDivElement
44

@@ -12,9 +12,11 @@ function getElement() {
1212

1313
export function useCustomEventListener<T>(
1414
eventName: string,
15-
eventHandler: (data: T) => void
15+
eventHandler: (data: T) => void,
16+
deps?: DependencyList
1617
): (el: HTMLElement | null) => void {
1718
let element: HTMLElement | null
19+
1820
useEffect(() => {
1921
element = element || getElement()
2022
const handleEvent = (event: CustomEvent | Event) => {
@@ -27,7 +29,7 @@ export function useCustomEventListener<T>(
2729
return () => {
2830
element?.removeEventListener(eventName, handleEvent, false)
2931
}
30-
}, [])
32+
}, deps)
3133

3234
return (el: HTMLElement | null) => {
3335
element = el

0 commit comments

Comments
 (0)