-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix observer and use single instance
- Loading branch information
Showing
7 changed files
with
207 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
class FakeIntersectionObserver { | ||
observe() {} | ||
unobserve() {} | ||
disconnect() {} | ||
} | ||
|
||
const MyIntersectionObserver = | ||
globalThis.IntersectionObserver || FakeIntersectionObserver; | ||
|
||
class ElementObserver { | ||
private observer; | ||
|
||
private elementsMap: Map<Element, Function> = new Map(); | ||
|
||
constructor() { | ||
this.observer = new MyIntersectionObserver(this.onObserved); | ||
} | ||
public onObserved = (entries: IntersectionObserverEntry[]) => { | ||
entries.forEach((entry) => { | ||
const elementCallback = this.elementsMap.get(entry.target as Element); | ||
if (elementCallback) { | ||
elementCallback(entry); | ||
} | ||
}); | ||
}; | ||
|
||
public registerCallback(element: Element, callback: Function) { | ||
this.observer.observe(element); | ||
this.elementsMap.set(element, callback); | ||
} | ||
|
||
public removeCallback(element: Element) { | ||
this.observer.unobserve(element); | ||
this.elementsMap.delete(element); | ||
} | ||
} | ||
|
||
export default ElementObserver; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { useCallback } from 'react'; | ||
import ElementObserver from './elementObserver'; | ||
|
||
let observer: ElementObserver; | ||
const getObserver = () => { | ||
if(!observer) { | ||
observer = new ElementObserver(); | ||
} | ||
return observer; | ||
} | ||
|
||
/** | ||
* Hook to listen for a ref element's resize events being triggered. When resized, | ||
* it sets state to an object of {width: number, height: number} indicating the contentRect | ||
* size of the element at the new resize. | ||
* | ||
* @param containerRef - Ref element to listen for resize events on | ||
* @returns - Size object with width and height attributes | ||
*/ | ||
export default function useIntersectionObserver() { | ||
const observe = useCallback((element: Element, callback: Function) => { | ||
const observer = getObserver(); | ||
observer.registerCallback(element, callback); | ||
}, []); | ||
|
||
const unobserve = useCallback((element: Element) => { | ||
const observer = getObserver(); | ||
observer.removeCallback(element); | ||
}, []); | ||
|
||
return { | ||
observe, | ||
unobserve, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// TODO move this | ||
const observe = jest.fn(); | ||
const unobserve = jest.fn(); | ||
const disconnect = jest.fn(); | ||
|
||
jest.spyOn(globalThis, 'IntersectionObserver').mockImplementation(() => { | ||
return { | ||
observe, | ||
unobserve, | ||
disconnect, | ||
root: null, | ||
thresholds: [], | ||
rootMargin: '', | ||
takeRecords: () => [], | ||
}; | ||
}); | ||
|
||
import ElementObserver from '../src/hooks/elementObserver'; | ||
|
||
describe('elementObserver', () => { | ||
it('registers a callback and observes the element', () => { | ||
const observer = new ElementObserver(); | ||
const element = document.createElement('li'); | ||
observer.registerCallback(element, ()=>{}); | ||
expect(observe).toHaveBeenCalled(); | ||
expect(observe).toHaveBeenCalledWith(element); | ||
}); | ||
|
||
it('unregisters a callback and unobserves the element', () => { | ||
const observer = new ElementObserver(); | ||
const element = document.createElement('li'); | ||
observer.removeCallback(element); | ||
expect(unobserve).toHaveBeenCalled(); | ||
expect(unobserve).toHaveBeenCalledWith(element); | ||
}); | ||
|
||
}); | ||
|
||
jest.clearAllMocks(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { renderHook, act } from '@testing-library/react-hooks'; | ||
import ElementObserver from '../src/hooks/elementObserver'; | ||
jest.mock('../src/hooks/elementObserver'); | ||
|
||
import useIntersectionObserver from '../src/hooks/useIntersectionObserver'; | ||
|
||
describe('useIntersectionObserver', () => { | ||
it('returns an object on initialization', () => { | ||
const { result } = renderHook(() => useIntersectionObserver()); | ||
expect(result.current).toBeDefined(); | ||
}); | ||
|
||
it('registers a callback', () => { | ||
const { result } = renderHook(() => useIntersectionObserver()); | ||
const element = document.createElement('li'); | ||
const callback = () => {}; | ||
act(() => { | ||
result.current.observe(element, callback); | ||
}); | ||
const mockElementObserver = (ElementObserver as jest.Mock).mock | ||
.instances[0]; | ||
const registerCallback = mockElementObserver.registerCallback; | ||
expect(registerCallback.mock.calls.length).toBe(1); | ||
expect(registerCallback.mock.calls[0].length).toBe(2); | ||
expect(registerCallback.mock.calls[0][0]).toBe(element); | ||
expect(registerCallback.mock.calls[0][1]).toBe(callback); | ||
}); | ||
|
||
it('unregisters a callback', () => { | ||
const { result } = renderHook(() => useIntersectionObserver()); | ||
const element = document.createElement('li'); | ||
act(() => { | ||
result.current.unobserve(element); | ||
}); | ||
const mockElementObserver = (ElementObserver as jest.Mock).mock | ||
.instances[0]; | ||
const removeCallback = mockElementObserver.removeCallback; | ||
expect(removeCallback.mock.calls.length).toBe(1); | ||
expect(removeCallback.mock.calls[0].length).toBe(1); | ||
expect(removeCallback.mock.calls[0][0]).toBe(element); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters