From e4af53daaf667047c075cfeda59ff76f8d8e415a Mon Sep 17 00:00:00 2001 From: du Date: Tue, 20 Jul 2021 23:48:49 +0800 Subject: [PATCH 1/2] refactor: transform test/components/Provider => ts --- .../{Provider.spec.js => Provider.spec.tsx} | 149 ++++++++++++------ 1 file changed, 100 insertions(+), 49 deletions(-) rename test/components/{Provider.spec.js => Provider.spec.tsx} (73%) diff --git a/test/components/Provider.spec.js b/test/components/Provider.spec.tsx similarity index 73% rename from test/components/Provider.spec.js rename to test/components/Provider.spec.tsx index 116570a1e..f482ef0b4 100644 --- a/test/components/Provider.spec.js +++ b/test/components/Provider.spec.tsx @@ -1,10 +1,13 @@ /*eslint-disable react/prop-types*/ -import React, { Component } from 'react' +import React, { Component, Dispatch } from 'react' import ReactDOM from 'react-dom' import { createStore } from 'redux' import { Provider, connect, ReactReduxContext } from '../../src/index' import * as rtl from '@testing-library/react' +import type { ReactReduxContextValue } from '../../src' +import type { Store } from 'redux' + import '@testing-library/jest-dom/extend-expect' const createExampleTextReducer = @@ -21,7 +24,8 @@ describe('React', () => { render() { return ( - {({ store }) => { + {(props) => { + let { store } = props as ReactReduxContextValue let text = '' if (store) { @@ -46,10 +50,6 @@ describe('React', () => { it('should not enforce a single child', () => { const store = createStore(() => ({})) - // Ignore propTypes warnings - const propTypes = Provider.propTypes - Provider.propTypes = {} - const spy = jest.spyOn(console, 'error').mockImplementation(() => {}) expect(() => @@ -59,7 +59,7 @@ describe('React', () => { ) ).not.toThrow() - + //@ts-expect-error expect(() => rtl.render()).not.toThrow( /children with exactly one child/ ) @@ -73,7 +73,6 @@ describe('React', () => { ) ).not.toThrow(/a single React element child/) spy.mockRestore() - Provider.propTypes = propTypes }) it('should add the store to context', () => { @@ -94,14 +93,18 @@ describe('React', () => { }) it('accepts new store in props', () => { - const store1 = createStore((state = 10) => state + 1) - const store2 = createStore((state = 10) => state * 2) - const store3 = createStore((state = 10) => state * state + 1) - - let externalSetState - class ProviderContainer extends Component { - constructor() { - super() + const store1 = createStore((state: number = 10) => state + 1) + const store2 = createStore((state: number = 10) => state * 2) + const store3 = createStore((state: number = 10) => state * state + 1) + + interface StateType { + store: Store + } + + let externalSetState: Dispatch + class ProviderContainer extends Component { + constructor(props: {}) { + super(props) this.state = { store: store1 } externalSetState = this.setState.bind(this) } @@ -156,33 +159,47 @@ describe('React', () => { }) it('should handle subscriptions correctly when there is nested Providers', () => { - const reducer = (state = 0, action) => + interface ActionType { + type: string + } + interface TStateProps { + count: number + } + const reducer = (state = 0, action: ActionType) => action.type === 'INC' ? state + 1 : state const innerStore = createStore(reducer) - const innerMapStateToProps = jest.fn((state) => ({ count: state })) - @connect(innerMapStateToProps) - class Inner extends Component { - render() { + const innerMapStateToProps = jest.fn((state) => ({ + count: state, + })) + class Inner extends Component { + render(): JSX.Element { return
{this.props.count}
} } + const WrapperInner = connect( + innerMapStateToProps + )(Inner) + const outerStore = createStore(reducer) - @connect((state) => ({ count: state })) class Outer extends Component { render() { return ( - + ) } } + const WrapperOuter = connect( + (state) => ({ count: state }) + )(Outer) + rtl.render( - + ) expect(innerMapStateToProps).toHaveBeenCalledTimes(1) @@ -195,11 +212,15 @@ describe('React', () => { }) it('should pass state consistently to mapState', () => { - function stringBuilder(prev = '', action) { + interface ActionType { + type: string + body: string + } + function stringBuilder(prev = '', action: ActionType) { return action.type === 'APPEND' ? prev + action.body : prev } - const store = createStore(stringBuilder) + const store: Store = createStore(stringBuilder) rtl.act(() => { store.dispatch({ type: 'APPEND', body: 'a' }) @@ -207,38 +228,53 @@ describe('React', () => { let childMapStateInvokes = 0 - @connect((state) => ({ state })) - class Container extends Component { - emitChange() { - store.dispatch({ type: 'APPEND', body: 'b' }) - } + const childCalls: Array> = [] + interface ChildContainerProps { + parentState: string + } + class ChildContainer extends Component { render() { - return ( -
- - -
- ) + return
} } - const childCalls = [] - @connect((state, parentProps) => { + const WrapperChildrenContainer = connect< + {}, + unknown, + ChildContainerProps, + string + >((state, parentProps) => { childMapStateInvokes++ childCalls.push([state, parentProps.parentState]) // The state from parent props should always be consistent with the current state return {} - }) - class ChildContainer extends Component { + })(ChildContainer) + + interface TStateProps { + state: string + } + class Container extends Component { + emitChange() { + store.dispatch({ type: 'APPEND', body: 'b' }) + } + render() { - return
+ return ( +
+ + +
+ ) } } + const WrapperContainer = connect( + (state) => ({ state }) + )(Container) const tester = rtl.render( - + ) @@ -320,29 +356,44 @@ describe('React', () => { }) it('should handle store and children change in a the same render', () => { + interface PropsType { + value: string + } + interface StateType { + nestedA: PropsType + nestedB: PropsType + } const reducerA = (state = { nestedA: { value: 'expectedA' } }) => state const reducerB = (state = { nestedB: { value: 'expectedB' } }) => state const storeA = createStore(reducerA) const storeB = createStore(reducerB) - @connect((state) => ({ value: state.nestedA.value })) - class ComponentA extends Component { + class ComponentA extends Component { render() { return
{this.props.value}
} } - @connect((state) => ({ value: state.nestedB.value })) - class ComponentB extends Component { + const WrapperComponentA = connect( + (state) => ({ + value: state.nestedA.value, + }) + )(ComponentA) + + class ComponentB extends Component { render() { return
{this.props.value}
} } + const WrapperComponentB = connect( + (state) => ({ value: state.nestedB.value }) + )(ComponentB) + const { getByTestId, rerender } = rtl.render( - + ) @@ -350,7 +401,7 @@ describe('React', () => { rerender( - + ) From cfa11fe71f85bcbda930cc0099eaced13bc92aa3 Mon Sep 17 00:00:00 2001 From: du Date: Tue, 20 Jul 2021 23:55:08 +0800 Subject: [PATCH 2/2] refactor: optimize the writing --- src/exports.ts | 2 ++ test/hooks/useDispatch.spec.tsx | 3 +-- test/hooks/useSelector.spec.tsx | 9 ++++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/exports.ts b/src/exports.ts index c8ef76a64..69b0aca1b 100644 --- a/src/exports.ts +++ b/src/exports.ts @@ -27,6 +27,7 @@ import { useSelector, createSelectorHook } from './hooks/useSelector' import { useStore, createStoreHook } from './hooks/useStore' import shallowEqual from './utils/shallowEqual' +import type { Subscription } from '../src/utils/Subscription' export * from './types' export type { @@ -46,6 +47,7 @@ export type { MapDispatchToPropsNonObject, MergeProps, ReactReduxContextValue, + Subscription, } export { Provider, diff --git a/test/hooks/useDispatch.spec.tsx b/test/hooks/useDispatch.spec.tsx index d475ac20d..5fecddee8 100644 --- a/test/hooks/useDispatch.spec.tsx +++ b/test/hooks/useDispatch.spec.tsx @@ -6,8 +6,7 @@ import { useDispatch, createDispatchHook, } from '../../src/index' -import type { ProviderProps } from '../../src/' -import type { ReactReduxContextValue } from '../../src/components/Context' +import type { ProviderProps, ReactReduxContextValue } from '../../src/' const store = createStore((c: number = 1): number => c + 1) const store2 = createStore((c: number = 1): number => c + 2) diff --git a/test/hooks/useSelector.spec.tsx b/test/hooks/useSelector.spec.tsx index b8c7697f4..a9d48f11c 100644 --- a/test/hooks/useSelector.spec.tsx +++ b/test/hooks/useSelector.spec.tsx @@ -14,9 +14,12 @@ import { import { useReduxContext } from '../../src/hooks/useReduxContext' import type { FunctionComponent, DispatchWithoutAction, ReactNode } from 'react' import type { Store, AnyAction } from 'redux' -import type { ProviderProps, TypedUseSelectorHook } from '../../src/' -import type { Subscription } from '../../src/utils/Subscription' -import type { ReactReduxContextValue } from '../../src/components/Context' +import type { + ProviderProps, + TypedUseSelectorHook, + ReactReduxContextValue, + Subscription, +} from '../../src/' describe('React', () => { describe('hooks', () => {