From 0120236c394dd49a8d4a84ad222e928038266dd3 Mon Sep 17 00:00:00 2001 From: Ian Obermiller Date: Thu, 6 Dec 2018 22:40:28 -0800 Subject: [PATCH] Add tests for unsubscribe and mapState changes --- src/__tests__/index-test.tsx | 40 ++++++++++++++++++++++++++++++++++-- src/typings.d.ts | 4 ++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/__tests__/index-test.tsx b/src/__tests__/index-test.tsx index cee5c0e..35b2880 100644 --- a/src/__tests__/index-test.tsx +++ b/src/__tests__/index-test.tsx @@ -14,6 +14,12 @@ interface IState { foo: string; } +// https://github.com/kentcdodds/react-testing-library/issues/215 +// useEffect is not triggered on re-renders +beforeAll(() => + jest.spyOn(React, 'useEffect').mockImplementation(React.useLayoutEffect)); +afterAll(() => (React.useEffect as any).mockRestore()); + describe('redux-react-hook', () => { let subscriberCallback: () => void; let state: IState; @@ -24,10 +30,10 @@ describe('redux-react-hook', () => { const createStore = (): Store => ({ dispatch: (action: any) => action, getState: () => state, - subscribe: (l: () => void) => { + subscribe: jest.fn((l: () => void) => { subscriberCallback = l; return cancelSubscription; - }, + }), // tslint:disable-next-line:no-empty replaceReducer() {}, }); @@ -83,6 +89,20 @@ describe('redux-react-hook', () => { expect(getText()).toBe('foo'); }); + it('cancels subscription on unmount', () => { + const mapState = (s: IState) => s.foo; + const Component = () => { + const foo = useMappedState(mapState); + return
{foo}
; + }; + + render(); + + ReactDOM.unmountComponentAtNode(reactRoot); + + expect(cancelSubscription).toHaveBeenCalled(); + }); + it('does not rerender if the selected state has not changed', () => { const mapState = (s: IState) => s.foo; let renderCount = 0; @@ -138,4 +158,20 @@ describe('redux-react-hook', () => { render(); expect(getText()).toBe('hello'); }); + + it('calls the correct mapState if mapState changes and the store updates', () => { + const Component = ({n}: {n: number}) => { + const mapState = React.useCallback((s: IState) => s.foo + ' ' + n, [n]); + const foo = useMappedState(mapState); + return
{foo}
; + }; + + render(); + render(); + + state = {...state, foo: 'foo'}; + subscriberCallback(); + + expect(getText()).toBe('foo 45'); + }); }); diff --git a/src/typings.d.ts b/src/typings.d.ts index f944f65..05fdfac 100644 --- a/src/typings.d.ts +++ b/src/typings.d.ts @@ -8,6 +8,10 @@ declare module 'react' { didUpdate: () => (() => void) | void, dependencies?: Array, ): void; + export function useLayoutEffect( + didUpdate: () => (() => void) | void, + dependencies?: Array, + ): void; export function useRef(initialValue?: T): {current: T}; export function useState( initialState: T | (() => T),