Repository: streamich/react-use
Pull request #932 (2020-01-27 21:04:15)

fix(useIntersection): some bugs
# Description

fix(useIntersection): return null if IntersectionObserver is not supported
fix(useIntersection): reset an intersectionObserverEntry when the ref changes
fix(useIntersection): disconnect an old IntersectionObserver instance when the ref changes

## Type of change

<!-- Check all relevant options. -->
- [x] Bug fix _(non-breaking change which fixes an issue)_
- [ ] New feature _(non-breaking change which adds functionality)_
- [ ] **Breaking change** _(fix or feature that would cause existing functionality to not work as before)_

# Checklist
- [x] Read the [Contributing Guide](https://github.com/streamich/react-use/blob/master/CONTRIBUTING.md)
- [x] Perform a code self-review
- [ ] Comment the code, particularly in hard-to-understand areas
- [ ] Add documentation
- [ ] Add hook's story at Storybook
- [x] Cover changes with tests
- [x] Ensure the test suite passes (`yarn test`)
- [ ] Provide 100% tests coverage
- [x] Make sure code lints (`yarn lint`). Fix it with `yarn lint:fix` in case of failure.
- [x] Make sure types are fine (`yarn lint:types`).

<!-- If you can't check all the checkboxes right now - check what you can, create a Draft PR, make some changes if needed and get back to it when you will be able to put some marks in list. -->



Patch:
diff --git a/src/useIntersection.ts b/src/useIntersection.ts
index 4a7b78ea..7cf9941d 100644
--- a/src/useIntersection.ts
+++ b/src/useIntersection.ts
@@ -7,7 +7,7 @@ const useIntersection = (
   const [intersectionObserverEntry, setIntersectionObserverEntry] = useState<IntersectionObserverEntry | null>(null);
 
   useEffect(() => {
-    if (ref.current) {
+    if (ref.current && typeof IntersectionObserver === 'function') {
       const handler = (entries: IntersectionObserverEntry[]) => {
         setIntersectionObserverEntry(entries[0]);
       };
@@ -16,13 +16,12 @@ const useIntersection = (
       observer.observe(ref.current);
 
       return () => {
-        if (ref.current) {
-          observer.disconnect();
-        }
+        setIntersectionObserverEntry(null);
+        observer.disconnect();
       };
     }
     return () => {};
-  }, [ref, options.threshold, options.root, options.rootMargin]);
+  }, [ref.current, options.threshold, options.root, options.rootMargin]);
 
   return intersectionObserverEntry;
 };


Test patch:
diff --git a/tests/useIntersection.test.tsx b/tests/useIntersection.test.tsx
index fb83108b..bdb7bc05 100644
--- a/tests/useIntersection.test.tsx
+++ b/tests/useIntersection.test.tsx
@@ -8,6 +8,10 @@ import { useIntersection } from '../src';
 
 beforeEach(() => {
   intersectionObserver.mock();
+  const IO = IntersectionObserver;
+  jest.spyOn(IO.prototype, 'disconnect');
+  jest.spyOn(global as any, 'IntersectionObserver');
+  IntersectionObserver.prototype = IO.prototype;
 });
 
 afterEach(() => {
@@ -45,6 +49,59 @@ describe('useIntersection', () => {
     expect(result.current).toBe(null);
   });
 
+  it('should reset an intersectionObserverEntry when the ref changes', () => {
+    TestUtils.act(() => {
+      targetRef = createRef();
+      ReactDOM.render(<div ref={targetRef} />, container);
+    });
+
+    const { result, rerender } = renderHook(() => useIntersection(targetRef, { root: container, threshold: 0.8 }));
+
+    const mockIntersectionObserverEntry = {
+      boundingClientRect: targetRef.current.getBoundingClientRect(),
+      intersectionRatio: 0.81,
+      intersectionRect: container.getBoundingClientRect(),
+      isIntersecting: true,
+      rootBounds: container.getBoundingClientRect(),
+      target: targetRef.current,
+      time: 300,
+    };
+    TestRenderer.act(() => {
+      intersectionObserver.simulate(mockIntersectionObserverEntry);
+    });
+
+    expect(result.current).toEqual(mockIntersectionObserverEntry);
+
+    targetRef.current = document.createElement('div');
+    rerender();
+
+    expect(result.current).toEqual(null);
+  });
+
+  it('should return null if IntersectionObserver is not supported', () => {
+    targetRef = createRef();
+    targetRef.current = document.createElement('div');
+    delete window.IntersectionObserver;
+
+    expect(() => renderHook(() => useIntersection(targetRef, {}))).not.toThrow();
+  });
+
+  it('should disconnect an old IntersectionObserver instance when the ref changes', () => {
+    targetRef = createRef();
+    targetRef.current = document.createElement('div');
+
+    const { rerender } = renderHook(() => useIntersection(targetRef, {}));
+
+    targetRef.current = document.createElement('div');
+    rerender();
+
+    targetRef.current = null;
+    rerender();
+
+    expect(IntersectionObserver).toHaveBeenCalledTimes(2);
+    expect(IntersectionObserver.prototype.disconnect).toHaveBeenCalledTimes(2);
+  });
+
   it('should return the first IntersectionObserverEntry when the IntersectionObserver registers an intersection', () => {
     TestUtils.act(() => {
       targetRef = createRef();