Repository: streamich/react-use
Pull request #794 (2019-11-22 10:56:14)

fix(#792): make useUnmount invoke the recent callback version
# Description

Fix: #792 
Previous hook version stored the first callback version and invoked only that version.

## 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
- [x] Comment the code, particularly in hard-to-understand areas
- [x] Add documentation
- [x] Add hook's story at Storybook
- [x] Cover changes with tests
- [x] Ensure the test suite passes (`yarn test`)
- [x] 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/useUnmount.ts b/src/useUnmount.ts
index c27bd7ea..2bd460e9 100644
--- a/src/useUnmount.ts
+++ b/src/useUnmount.ts
@@ -1,7 +1,13 @@
+import { useRef } from 'react';
 import useEffectOnce from './useEffectOnce';
 
-const useUnmount = (fn: () => void | undefined) => {
-  useEffectOnce(() => fn);
+const useUnmount = (fn: () => any): void => {
+  const fnRef = useRef(fn);
+
+  // update the ref each render so if it change the newest callback will be invoked
+  fnRef.current = fn;
+
+  useEffectOnce(() => () => fnRef.current());
 };
 
 export default useUnmount;


Test patch:
diff --git a/tests/useUnmount.test.ts b/tests/useUnmount.test.ts
index 2c6078a9..77fe892e 100644
--- a/tests/useUnmount.test.ts
+++ b/tests/useUnmount.test.ts
@@ -1,32 +1,51 @@
 import { renderHook } from '@testing-library/react-hooks';
 import { useUnmount } from '../src';
 
-const mockCallback = jest.fn();
+describe('useUnmount', () => {
+  it('should be defined', () => {
+    expect(useUnmount).toBeDefined();
+  });
 
-afterEach(() => {
-  jest.resetAllMocks();
-});
+  it('should not call provided callback on mount', () => {
+    const spy = jest.fn();
+    renderHook(() => useUnmount(spy));
 
-it('should not call provided callback on mount', () => {
-  renderHook(() => useUnmount(mockCallback));
+    expect(spy).not.toHaveBeenCalled();
+  });
 
-  expect(mockCallback).not.toHaveBeenCalled();
-});
+  it('should not call provided callback on re-renders', () => {
+    const spy = jest.fn();
+    const hook = renderHook(() => useUnmount(spy));
 
-it('should call provided callback on unmount', () => {
-  const { unmount } = renderHook(() => useUnmount(mockCallback));
-  expect(mockCallback).not.toHaveBeenCalled();
+    hook.rerender();
+    hook.rerender();
+    hook.rerender();
+    hook.rerender();
 
-  unmount();
+    expect(spy).not.toHaveBeenCalled();
+  });
 
-  expect(mockCallback).toHaveBeenCalledTimes(1);
-});
+  it('should call provided callback on unmount', () => {
+    const spy = jest.fn();
+    const hook = renderHook(() => useUnmount(spy));
+
+    hook.unmount();
+
+    expect(spy).toHaveBeenCalledTimes(1);
+  });
 
-it('should not call provided callback on rerender', () => {
-  const { rerender } = renderHook(() => useUnmount(mockCallback));
-  expect(mockCallback).not.toHaveBeenCalled();
+  it('should call provided callback if is has been changed', () => {
+    const spy = jest.fn();
+    const spy2 = jest.fn();
+    const spy3 = jest.fn();
+    const hook = renderHook(cb => useUnmount(cb), { initialProps: spy });
 
-  rerender();
+    hook.rerender(spy2);
+    hook.rerender(spy3);
+    hook.unmount();
 
-  expect(mockCallback).not.toHaveBeenCalled();
+    expect(spy).not.toHaveBeenCalled();
+    expect(spy2).not.toHaveBeenCalled();
+    expect(spy3).toHaveBeenCalledTimes(1);
+  });
 });