Skip to content

Commit 44e2c46

Browse files
authored
Merge pull request #132 from dogfessional/fix-touch-event-cleanup
Fix touch event cleanup
2 parents 3932868 + edd8387 commit 44e2c46

File tree

4 files changed

+71
-28
lines changed

4 files changed

+71
-28
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-swipeable",
3-
"version": "5.1.0-alpha.1",
3+
"version": "5.1.0-alpha.2",
44
"description": "Swipe and touch handlers for react",
55
"main": "./lib/index.js",
66
"module": "es/index.js",
@@ -21,7 +21,7 @@
2121
},
2222
"size-limit": [
2323
{
24-
"limit": "1.8 KB",
24+
"limit": "1.875 KB",
2525
"path": "es/index.js"
2626
}
2727
],

src/__tests__/index.spec.js

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const expectSwipeFuncsDir = (sf, dir) =>
3535
}
3636
})
3737

38-
function mockListenerSetup(el) {
38+
function mockListenersSetup(el) {
3939
// track eventListener adds to trigger later
4040
// idea from - https://github.com/airbnb/enzyme/issues/426#issuecomment-228601631
4141
const eventListenerMap = {}
@@ -62,13 +62,13 @@ function SwipeableUsingHook(props) {
6262
)
6363
}
6464

65-
function setupGetMountedComponent(type) {
65+
function setupGetMountedComponent(type, mockListeners = mockListenersSetup) {
6666
return props => {
6767
let wrapper
6868
let eventListenerMap
6969
const innerRef = el => {
7070
// don't re-assign eventlistener map
71-
if (!eventListenerMap) eventListenerMap = mockListenerSetup(el)
71+
if (!eventListenerMap) eventListenerMap = mockListeners(el)
7272
}
7373
if (type === 'Swipeable') {
7474
wrapper = mount(
@@ -100,7 +100,7 @@ function setupGetMountedComponent(type) {
100100
beforeEach(() => {
101101
// track eventListener adds to trigger later
102102
// idea from - https://github.com/airbnb/enzyme/issues/426#issuecomment-228601631
103-
eventListenerMapDocument = mockListenerSetup(document)
103+
eventListenerMapDocument = mockListenersSetup(document)
104104
})
105105
afterAll(() => {
106106
document.eventListener = origAddEventListener
@@ -381,6 +381,33 @@ function setupGetMountedComponent(type) {
381381
expect(swipeFuncs[`onSwiped${dir}`]).not.toHaveBeenCalled()
382382
})
383383
})
384+
385+
it('Cleans up and re-attachs touch event listeners', () => {
386+
let spies
387+
const mockListeners = el => {
388+
// already spying
389+
if (spies) return
390+
spies = {}
391+
spies.addEventListener = jest.spyOn(el, 'addEventListener')
392+
spies.removeEventListener = jest.spyOn(el, 'removeEventListener')
393+
}
394+
const { wrapper } = setupGetMountedComponent(type, mockListeners)({})
395+
expect(spies.addEventListener).toHaveBeenCalledTimes(3)
396+
expect(spies.removeEventListener).not.toHaveBeenCalled()
397+
wrapper.setProps({ trackTouch: false })
398+
expect(spies.addEventListener).toHaveBeenCalledTimes(3)
399+
expect(spies.removeEventListener).toHaveBeenCalledTimes(3)
400+
// VERIFY REMOVED HANDLERS ARE THE SAME ONES THAT WERE ADDED!
401+
expect(spies.addEventListener.mock.calls.length).toBe(3)
402+
spies.addEventListener.mock.calls.forEach((call, idx) => {
403+
expect(spies.removeEventListener.mock.calls[idx][0]).toBe(call[0])
404+
expect(spies.removeEventListener.mock.calls[idx][1]).toBe(call[1])
405+
})
406+
407+
wrapper.setProps({ trackTouch: true })
408+
expect(spies.addEventListener).toHaveBeenCalledTimes(6)
409+
expect(spies.removeEventListener).toHaveBeenCalledTimes(3)
410+
})
384411
})
385412
})
386413

src/index.js

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -112,22 +112,24 @@ function getHandlers(set, props) {
112112
})
113113
}
114114

115-
const stop = () => {
115+
const cleanUpMouse = () => {
116116
// safe to just call removeEventListener
117117
document.removeEventListener(mouseMove, onMove)
118118
document.removeEventListener(mouseUp, onUp)
119119
}
120120

121121
const onUp = e => {
122-
stop()
122+
cleanUpMouse()
123123
onEnd(e)
124124
}
125125

126-
const cleanUp = el => {
127-
if (el && el.removeEventListener) {
128-
el.removeEventListener(touchStart, onStart)
129-
el.removeEventListener(touchMove, onMove)
130-
el.removeEventListener(touchEnd, onUp)
126+
const attachTouch = el => {
127+
if (el && el.addEventListener) {
128+
// attach touch event listeners and handlers
129+
const tls = [[touchStart, onStart], [touchMove, onMove], [touchEnd, onEnd]]
130+
tls.forEach(([e, h]) => el.addEventListener(e, h))
131+
// return properly scoped cleanup method for removing listeners
132+
return () => tls.forEach(([e, h]) => el.removeEventListener(e, h))
131133
}
132134
}
133135

@@ -138,22 +140,39 @@ function getHandlers(set, props) {
138140
set(state => {
139141
// if the same DOM el as previous just return state
140142
if (state.el === el) return state
141-
// if new DOM el clean up old DOM
142-
if (state.el && state.el !== el) cleanUp(state.el)
143+
144+
let addState = {}
145+
// if new DOM el clean up old DOM and reset cleanUpTouch
146+
if (state.el && state.el !== el && state.cleanUpTouch) {
147+
state.cleanUpTouch()
148+
addState.cleanUpTouch = null
149+
}
143150
// only attach if we want to track touch
144-
if (state.props.trackTouch) {
145-
if (el && el.addEventListener) {
146-
el.addEventListener(touchStart, onStart)
147-
el.addEventListener(touchMove, onMove)
148-
el.addEventListener(touchEnd, onUp)
149-
// store event attached DOM el for comparison
150-
return { ...state, el }
151-
}
151+
if (state.props.trackTouch && el) {
152+
addState.cleanUpTouch = attachTouch(el)
152153
}
153-
return state
154+
155+
// store event attached DOM el for comparison, clean up, and re-attachment
156+
return { ...state, el, ...addState }
154157
})
155158
}
156159

160+
// update state, props, and handlers
161+
set(state => {
162+
let addState = {}
163+
// clean up touch handlers if no longer tracking touches
164+
if (!props.trackTouch && state.cleanUpTouch) {
165+
state.cleanUpTouch()
166+
addState.cleanUpTouch = null
167+
} else if (props.trackTouch && !state.cleanUpTouch) {
168+
// attach/re-attach touch handlers
169+
if (state.el) {
170+
addState.cleanUpTouch = attachTouch(state.el)
171+
}
172+
}
173+
return { ...state, props, ...addState }
174+
})
175+
157176
// set ref callback to attach touch event listeners
158177
const output = { ref: onRef }
159178

@@ -162,9 +181,6 @@ function getHandlers(set, props) {
162181
output.onMouseDown = onStart
163182
}
164183

165-
// update props
166-
set(state => ({ ...state, props }))
167-
168184
return output
169185
}
170186

0 commit comments

Comments
 (0)