@@ -2090,7 +2339,13 @@ describe('React', () => {
}
const ConnectedComp2 = connect((state) => state)(Comp2)
- const Comp1 = ({ children, first }) => {
+ type Comp1TStatePropsType = Store1State1Type
+ type Comp1NoDispatchType = NoDispatchType
+ type Comp1OwnPropsType = {}
+ interface Comp1Props extends Comp1TStatePropsType {
+ children: JSX.Element | JSX.Element[]
+ }
+ const Comp1 = ({ children, first }: Comp1Props) => {
c1Spy()
return (
@@ -2099,9 +2354,17 @@ describe('React', () => {
)
}
- const ConnectedComp1 = connect((state) => state)(Comp1)
-
- const reducer1 = (state = { first: '1' }, action) => {
+ const ConnectedComp1 = connect<
+ Comp1TStatePropsType,
+ Comp1NoDispatchType,
+ Comp1OwnPropsType,
+ Store1State1Type
+ >((state) => state)(Comp1)
+
+ const reducer1 = (
+ state: Store1State1Type = { first: '1' },
+ action: ActionType
+ ) => {
switch (action.type) {
case 'CHANGE':
return { first: '2' }
@@ -2110,7 +2373,10 @@ describe('React', () => {
}
}
- const reducer2 = (state = { second: '3' }, action) => {
+ const reducer2 = (
+ state: Store2State1Type = { second: '3' },
+ action: ActionType
+ ) => {
switch (action.type) {
case 'CHANGE':
return { second: '4' }
@@ -2125,6 +2391,10 @@ describe('React', () => {
const tester = rtl.render(
+ {/*TODO: Since the connect type does not support this advanced usage,
+ we will ignore it for the time being and resolve it after merging connect and connectAdvanced
+ https://github.com/reduxjs/react-redux/pull/1781 */}
+ {/*// @ts-ignore */}
@@ -2167,55 +2437,77 @@ describe('React', () => {
})
it('should subscribe properly when a new store is provided via props', () => {
- const store1 = createStore((state = 0, action) =>
- action.type === 'INC' ? state + 1 : state
+ type RootStateType = number
+ interface ActionType {
+ type: string
+ }
+ const store1 = createStore(
+ (state: RootStateType = 0, action: ActionType) =>
+ action.type === 'INC' ? state + 1 : state
)
- const store2 = createStore((state = 0, action) =>
- action.type === 'INC' ? state + 1 : state
+ const store2 = createStore(
+ (state: RootStateType = 0, action: ActionType) =>
+ action.type === 'INC' ? state + 1 : state
)
- const customContext = React.createContext()
+ const customContext =
+ React.createContext
(null)
- @connect((state) => ({ count: state }), undefined, undefined, {
- context: customContext,
- })
class A extends Component {
render() {
- return
+ return
}
}
+ const ConnectedA = connect(
+ (state) => ({ count: state }),
+ undefined,
+ undefined,
+ {
+ context: customContext,
+ }
+ )(A)
const mapStateToPropsB = jest.fn((state) => ({ count: state }))
- @connect(mapStateToPropsB, undefined, undefined, {
- context: customContext,
- })
class B extends Component {
render() {
- return
+ return
}
}
+ const ConnectedB = connect(mapStateToPropsB, undefined, undefined, {
+ context: customContext,
+ })(B)
const mapStateToPropsC = jest.fn((state) => ({ count: state }))
- @connect(mapStateToPropsC, undefined, undefined, {
- context: customContext,
- })
class C extends Component {
render() {
- return
+ return
}
}
+ const ConnectedC = connect(mapStateToPropsC, undefined, undefined, {
+ context: customContext,
+ })(C)
+ interface DTStatePropsType {
+ count: number
+ }
+ type DNoDispatchType = {}
+ type DOwnPropsType = {}
const mapStateToPropsD = jest.fn((state) => ({ count: state }))
- @connect(mapStateToPropsD)
- class D extends Component {
+ class D extends Component {
render() {
return {this.props.count}
}
}
+ const ConnectedD = connect<
+ DTStatePropsType,
+ DNoDispatchType,
+ DOwnPropsType,
+ RootStateType
+ >(mapStateToPropsD)(D)
rtl.render(
-
+
)
@@ -2241,40 +2533,6 @@ describe('React', () => {
})
describe('Refs', () => {
- it.skip('should throw when trying to access the wrapped instance if withRef is not specified', () => {
- const store = createStore(() => ({}))
-
- class Container extends Component {
- render() {
- return
- }
- }
-
- const decorator = connect((state) => state)
- const Decorated = decorator(Container)
-
- class Wrapper extends Component {
- render() {
- return (
- comp && comp.getWrappedInstance()} />
- )
- }
- }
-
- // TODO Remove this when React is fixed, per https://github.com/facebook/react/issues/11098
- const spy = jest.spyOn(console, 'error').mockImplementation(() => {})
- expect(() =>
- rtl.render(
-
-
-
- )
- ).toThrow(
- `To access the wrapped instance, you need to specify { withRef: true } in the options argument of the connect() call`
- )
- spy.mockRestore()
- })
-
it('should return the instance of the wrapped component for use in calling child methods', async (done) => {
const store = createStore(() => ({}))
@@ -2297,7 +2555,7 @@ describe('React', () => {
})
const Decorated = decorator(Container)
- const ref = React.createRef()
+ const ref = React.createRef()
class Wrapper extends Component {
render() {
@@ -2313,11 +2571,12 @@ describe('React', () => {
await tester.findByTestId('loaded')
- expect(ref.current.someInstanceMethod()).toBe(someData)
+ expect(ref.current!.someInstanceMethod()).toBe(someData)
done()
})
it('should correctly separate and pass through props to the wrapped component with a forwarded ref', () => {
+ type RootStateType = {}
const store = createStore(() => ({}))
class Container extends Component {
@@ -2325,13 +2584,20 @@ describe('React', () => {
return
}
}
-
- const decorator = connect((state) => state, null, null, {
+ type ContainerTStatePropsType = {}
+ type ContainerNoDispatchType = null
+ type ContainerOwnPropsType = { a: number }
+ const decorator = connect<
+ ContainerTStatePropsType,
+ ContainerNoDispatchType,
+ ContainerOwnPropsType,
+ RootStateType
+ >((state) => state, null, null, {
forwardRef: true,
})
const Decorated = decorator(Container)
- const ref = React.createRef()
+ const ref = React.createRef()
class Wrapper extends Component {
render() {
@@ -2375,7 +2641,7 @@ describe('React', () => {
})
const Decorated = decorator(Container)
- const ref = React.createRef()
+ const ref = React.createRef()
class Wrapper extends Component {
render() {
@@ -2391,7 +2657,7 @@ describe('React', () => {
await tester.findByTestId('loaded')
- expect(ref.current.someInstanceMethod()).toBe(someData)
+ expect(ref.current!.someInstanceMethod()).toBe(someData)
done()
})
@@ -2399,6 +2665,7 @@ describe('React', () => {
const store = createStore(() => ({}))
class ImpureComponent extends Component {
+ static contextTypes: any
render() {
return
}
@@ -2410,11 +2677,15 @@ describe('React', () => {
const decorator = connect((state) => state, null, null, { pure: false })
const Decorated = decorator(ImpureComponent)
+ interface StateFulWrapperStateType {
+ value: number
+ }
+ let externalSetState: Dispatch
- let externalSetState
- class StatefulWrapper extends Component {
- constructor() {
- super()
+ class StatefulWrapper extends Component<{}, StateFulWrapperStateType> {
+ static childContextTypes: any
+ constructor(props: {}) {
+ super(props)
this.state = { value: 0 }
externalSetState = this.setState.bind(this)
}
@@ -2441,11 +2712,17 @@ describe('React', () => {
)
expect(tester.getByTestId('statefulValue')).toHaveTextContent('0')
+ //@ts-ignore
externalSetState({ value: 1 })
expect(tester.getByTestId('statefulValue')).toHaveTextContent('1')
})
it('calls mapState and mapDispatch for impure components', () => {
+ type RootStateType = {
+ foo: string
+ bar: string
+ [x: string]: string
+ }
const store = createStore(() => ({
foo: 'foo',
bar: 'bar',
@@ -2455,14 +2732,25 @@ describe('React', () => {
const mapDispatchSpy = jest.fn().mockReturnValue({})
const impureRenderSpy = jest.fn()
- class ImpureComponent extends Component {
+ interface ImpureTStatePropsType {
+ value: string
+ }
+ type ImpureNoDispatch = {}
+ interface ImpureOwnProps {
+ storeGetter: { storeKey: string }
+ }
+ class ImpureComponent extends Component {
render() {
impureRenderSpy()
return
}
}
-
- const decorator = connect(
+ const decorator = connect<
+ ImpureTStatePropsType,
+ ImpureNoDispatch,
+ ImpureOwnProps,
+ RootStateType
+ >(
(state, { storeGetter }) => {
mapStateSpy()
return { value: state[storeGetter.storeKey] }
@@ -2474,11 +2762,17 @@ describe('React', () => {
const Decorated = decorator(ImpureComponent)
let externalSetState
- let storeGetter
- class StatefulWrapper extends Component {
- constructor() {
- super()
- storeGetter = { storeKey: 'foo' }
+ let storeGetter = { storeKey: 'foo' }
+ type StatefulWrapperStateType = {
+ storeGetter: typeof storeGetter
+ }
+ type StatefulWrapperPropsType = {}
+ class StatefulWrapper extends Component<
+ StatefulWrapperPropsType,
+ StatefulWrapperStateType
+ > {
+ constructor(props: StatefulWrapperPropsType) {
+ super(props)
this.state = {
storeGetter,
}
@@ -2508,6 +2802,7 @@ describe('React', () => {
// Impure update
storeGetter.storeKey = 'bar'
+ //@ts-ignore
externalSetState({ storeGetter })
// 4) After the the impure update
@@ -2523,17 +2818,19 @@ describe('React', () => {
const store = createStore(() => ({}))
let renderCount = 0
- @connect(() => ({}), null, null, { pure: false })
class ImpureComponent extends React.Component {
render() {
++renderCount
return
}
}
+ const ConnectedImpure = connect(() => ({}), null, null, {
+ pure: false,
+ })(ImpureComponent)
rtl.render(
-
+
)
@@ -2549,20 +2846,22 @@ describe('React', () => {
let store = createStore(() => ({}))
let renderCount = 0
- @connect(null, null, () => ({ a: 1 }), { pure: false })
class Container extends React.Component {
render() {
++renderCount
return
}
}
+ const ConnectedContainer = connect(null, null, () => ({ a: 1 }), {
+ pure: false,
+ })(Container)
class Parent extends React.Component {
componentDidMount() {
this.forceUpdate()
}
render() {
- return
+ return
}
}
@@ -2584,9 +2883,20 @@ describe('React', () => {
let memoizedReturnCount = 0
const store = createStore(() => ({ value: 1 }))
+ interface RootStateType {
+ value: number
+ }
+ interface TStatePropsType {
+ someObject: { prop: string; stateVal: number }
+ }
+ type NoDispatch = {}
+ interface OnwPropsType {
+ name: string
+ }
+
const mapStateFactory = () => {
- let lastProp, lastVal, lastResult
- return (state, props) => {
+ let lastProp: string, lastVal: number, lastResult: TStatePropsType
+ return (state: RootStateType, props: OnwPropsType) => {
if (props.name === lastProp && lastVal === state.value) {
memoizedReturnCount++
return lastResult
@@ -2599,7 +2909,6 @@ describe('React', () => {
}
}
- @connect(mapStateFactory)
class Container extends Component {
componentDidUpdate() {
updatedCount++
@@ -2608,12 +2917,18 @@ describe('React', () => {
return
}
}
+ const ConnectedContainer = connect<
+ TStatePropsType,
+ NoDispatch,
+ OnwPropsType,
+ RootStateType
+ >(mapStateFactory)(Container)
rtl.render(
-
-
+
+
)
@@ -2627,31 +2942,45 @@ describe('React', () => {
})
it('should allow a mapStateToProps factory consuming just state to return a function that gets ownProps', () => {
+ interface RootStateType {
+ value: number
+ }
+ type TStateProps = {}
+ type NoDispatch = {}
+ interface OwnProps {
+ name: string
+ }
const store = createStore(() => ({ value: 1 }))
let initialState
let initialOwnProps
- let secondaryOwnProps
- const mapStateFactory = function (factoryInitialState) {
+ let secondaryOwnProps: OwnProps
+ const mapStateFactory = function (factoryInitialState: RootStateType) {
initialState = factoryInitialState
initialOwnProps = arguments[1]
- return (state, props) => {
+ return (state: RootStateType, props: OwnProps) => {
secondaryOwnProps = props
return {}
}
}
- @connect(mapStateFactory)
class Container extends Component {
render() {
return
}
}
+ const ConnectedContainer = connect<
+ TStateProps,
+ NoDispatch,
+ OwnProps,
+ RootStateType
+ >(mapStateFactory)(Container)
+
rtl.render(
-
+
)
@@ -2662,18 +2991,32 @@ describe('React', () => {
expect(initialOwnProps).toBe(undefined)
expect(initialState).not.toBe(undefined)
+ //@ts-ignore
expect(secondaryOwnProps).not.toBe(undefined)
+ //@ts-ignore
expect(secondaryOwnProps.name).toBe('a')
})
it('should allow providing a factory function to mapDispatchToProps', () => {
let updatedCount = 0
let memoizedReturnCount = 0
+
const store = createStore(() => ({ value: 1 }))
+ type PassTStateProps = {}
+ interface TDispatchPropsType {
+ someObject: {
+ dispatchFn: ReduxDispatch
+ }
+ }
+ interface OwnPropsType {
+ count: number
+ name: string
+ }
+ type TMergeProps = Omit & TDispatchPropsType
const mapDispatchFactory = () => {
- let lastProp, lastResult
- return (dispatch, props) => {
+ let lastProp: string, lastResult: TDispatchPropsType
+ return (dispatch: ReduxDispatch, props: OwnPropsType) => {
if (props.name === lastProp) {
memoizedReturnCount++
return lastResult
@@ -2682,11 +3025,14 @@ describe('React', () => {
return (lastResult = { someObject: { dispatchFn: dispatch } })
}
}
- function mergeParentDispatch(stateProps, dispatchProps, parentProps) {
+ function mergeParentDispatch(
+ stateProps: PassTStateProps,
+ dispatchProps: TDispatchPropsType,
+ parentProps: OwnPropsType
+ ): TMergeProps {
return { ...stateProps, ...dispatchProps, name: parentProps.name }
}
- @connect(null, mapDispatchFactory, mergeParentDispatch)
class Passthrough extends Component {
componentDidUpdate() {
updatedCount++
@@ -2695,9 +3041,26 @@ describe('React', () => {
return
}
}
-
- class Container extends Component {
- constructor(props) {
+ const ConnectedPass = connect<
+ PassTStateProps,
+ TDispatchPropsType,
+ OwnPropsType,
+ TMergeProps
+ >(
+ null,
+ mapDispatchFactory,
+ mergeParentDispatch
+ )(Passthrough)
+
+ type ContainerPropsType = {}
+ interface ContainerStateType {
+ count: number
+ }
+ class Container extends Component<
+ ContainerPropsType,
+ ContainerStateType
+ > {
+ constructor(props: ContainerPropsType) {
super(props)
this.state = { count: 0 }
}
@@ -2708,8 +3071,8 @@ describe('React', () => {
const { count } = this.state
return (
)
}
@@ -2731,7 +3094,7 @@ describe('React', () => {
})
describe('Error handling for invalid arguments', () => {
- function renderWithBadConnect(Component) {
+ function renderWithBadConnect(Component: ElementType) {
const store = createStore(() => ({}))
const spy = jest.spyOn(console, 'error').mockImplementation(() => {})
@@ -2742,7 +3105,8 @@ describe('React', () => {
)
return null
- } catch (error) {
+ //@ts-ignore before typescript4.0, a catch could not have type annotations
+ } catch (error: any) {
return error.message
} finally {
spy.mockRestore()
@@ -2750,6 +3114,7 @@ describe('React', () => {
}
it('should throw a helpful error for invalid mapStateToProps arguments', () => {
+ //@ts-expect-error
@connect('invalid')
class InvalidMapState extends React.Component {
render() {
@@ -2764,6 +3129,7 @@ describe('React', () => {
})
it('should throw a helpful error for invalid mapDispatchToProps arguments', () => {
+ //@ts-expect-error
@connect(null, 'invalid')
class InvalidMapDispatch extends React.Component {
render() {
@@ -2778,6 +3144,7 @@ describe('React', () => {
})
it('should throw a helpful error for invalid mergeProps arguments', () => {
+ // @ts-expect-error
@connect(null, null, 'invalid')
class InvalidMerge extends React.Component {
render() {
@@ -2798,19 +3165,21 @@ describe('React', () => {
return
}
const spy = jest.spyOn(console, 'error').mockImplementation(() => {})
- const store = createStore(stringBuilder)
+ const store: Store = createStore(stringBuilder)
- @connect((state) => ({ string: state }))
class Container extends Component {
render() {
return
}
}
+ const ConnectedContainer = connect((state) => ({ string: state }))(
+ Container
+ )
rtl.render(
-
+
)
@@ -2821,7 +3190,8 @@ describe('React', () => {
describe('Subscription and update timing correctness', () => {
it('should pass state consistently to mapState', () => {
- const store = createStore(stringBuilder)
+ type RootStateType = string
+ const store: Store = createStore(stringBuilder)
rtl.act(() => {
store.dispatch({ type: 'APPEND', body: 'a' })
@@ -2829,8 +3199,10 @@ describe('React', () => {
let childMapStateInvokes = 0
- @connect((state) => ({ state }))
- class Container extends Component {
+ type ContainerStateProps = { state: string }
+ type ContainerNoDisPatch = {}
+ type ContainerOwnProps = {}
+ class Container extends Component {
emitChange() {
store.dispatch({ type: 'APPEND', body: 'b' })
}
@@ -2839,29 +3211,46 @@ describe('React', () => {
return (
-
+
)
}
}
+ const ConnectedContainer = connect<
+ ContainerStateProps,
+ ContainerNoDisPatch,
+ ContainerOwnProps,
+ RootStateType
+ >((state) => ({ state }))(Container)
- const childCalls = []
- @connect((state, parentProps) => {
- childMapStateInvokes++
- childCalls.push([state, parentProps.parentState])
- // The state from parent props should always be consistent with the current state
- expect(state).toEqual(parentProps.parentState)
- return {}
- })
+ const childCalls: any[] = []
+
+ type ChildrenTStateProps = {}
+ type ChildrenNoDisPatch = {}
+ type ChildrenOwnProps = {
+ parentState: string
+ }
class ChildContainer extends Component {
render() {
return
}
}
+ const ConnectedChildrenContainer = connect<
+ ChildrenTStateProps,
+ ChildrenNoDisPatch,
+ ChildrenOwnProps,
+ RootStateType
+ >((state, parentProps) => {
+ childMapStateInvokes++
+ childCalls.push([state, parentProps.parentState])
+ // The state from parent props should always be consistent with the current state
+ expect(state).toEqual(parentProps.parentState)
+ return {}
+ })(ChildContainer)
const tester = rtl.render(
-
+
)
@@ -2896,51 +3285,76 @@ describe('React', () => {
})
it('should invoke mapState always with latest props', () => {
- const store = createStore((state = 0) => state + 1)
+ type RootState = number
+ const store = createStore((state: RootState = 0) => state + 1)
- let propsPassedIn
+ interface InnerTStatePropsType {
+ reduxCount: RootState
+ }
+ type NoDispatchType = {}
+ interface OwnPropsType {
+ count: number
+ }
+ let propsPassedIn: OwnPropsType & InnerTStatePropsType
- @connect((reduxCount) => {
- return { reduxCount }
- })
- class InnerComponent extends Component {
+ class InnerComponent extends Component<
+ OwnPropsType & InnerTStatePropsType
+ > {
render() {
propsPassedIn = this.props
return
}
}
+ const ConnectedInner = connect<
+ InnerTStatePropsType,
+ NoDispatchType,
+ OwnPropsType,
+ RootState
+ >((reduxCount) => {
+ return { reduxCount }
+ })(InnerComponent)
- class OuterComponent extends Component {
- constructor() {
- super()
+ interface OuterStateType {
+ count: number
+ }
+ class OuterComponent extends Component<{}, OuterStateType> {
+ constructor(props: {}) {
+ super(props)
this.state = { count: 0 }
}
render() {
- return
+ return
}
}
- let outerComponent
+ let outerComponent = React.createRef()
rtl.render(
- (outerComponent = c)} />
+
)
- outerComponent.setState(({ count }) => ({ count: count + 1 }))
+ outerComponent.current!.setState(({ count }) => ({ count: count + 1 }))
store.dispatch({ type: '' })
-
+ //@ts-ignore
expect(propsPassedIn.count).toEqual(1)
+ //@ts-ignore
expect(propsPassedIn.reduxCount).toEqual(2)
})
it('should use the latest props when updated between actions', () => {
- const reactCallbackMiddleware = (store) => {
- let callback
+ type RootStateType = number
+ type PayloadType = () => void
+ interface ActionType {
+ type: string
+ payload?: PayloadType
+ }
+ const reactCallbackMiddleware = (store: MiddlewareAPI) => {
+ let callback: () => void
- return (next) => (action) => {
+ return (next: ReduxDispatch) => (action: ActionType) => {
if (action.type === 'SET_COMPONENT_CALLBACK') {
- callback = action.payload
+ callback = action.payload!
}
if (callback && action.type === 'INC1') {
@@ -2962,7 +3376,7 @@ describe('React', () => {
}
}
- const counter = (state = 0, action) => {
+ const counter = (state: RootStateType = 0, action: ActionType) => {
if (action.type === 'INC1') {
return state + 1
} else if (action.type === 'INC2') {
@@ -2976,7 +3390,21 @@ describe('React', () => {
applyMiddleware(reactCallbackMiddleware)
)
- const Child = connect((count) => ({ count }))(function (props) {
+ interface ChildrenTStatePropsType {
+ count: RootStateType
+ }
+ type ChildrenNoDispatchType = {}
+ interface ChildrenOwnProps {
+ prop: string
+ }
+ const Child = connect<
+ ChildrenTStatePropsType,
+ ChildrenNoDispatchType,
+ ChildrenOwnProps,
+ RootStateType
+ >((count) => ({ count }))(function (
+ props: ChildrenTStatePropsType & ChildrenOwnProps
+ ) {
return (
{
/>
)
})
- class Parent extends Component {
- constructor() {
- super()
+ type ParentPropsType = {}
+ interface ParentStateType {
+ prop: string
+ }
+ class Parent extends Component
{
+ inc1: () => void
+ constructor(props: {}) {
+ super(props)
this.state = {
prop: 'a',
}
@@ -3007,13 +3440,13 @@ describe('React', () => {
}
}
- let parent
- const rendered = rtl.render( (parent = ref)} />)
+ let parent = React.createRef()
+ const rendered = rtl.render()
expect(rendered.getByTestId('child').dataset.count).toEqual('0')
expect(rendered.getByTestId('child').dataset.prop).toEqual('a')
// Force the multi-update sequence by running this bound action creator
- parent.inc1()
+ parent.current!.inc1()
// The connected child component _should_ have rendered with the latest Redux
// store value (3) _and_ the latest wrapper prop ('b').
@@ -3022,51 +3455,70 @@ describe('React', () => {
})
it('should invoke mapState always with latest store state', () => {
- const store = createStore((state = 0) => state + 1)
+ type RootStateType = number
+ const store = createStore((state: RootStateType = 0) => state + 1)
let reduxCountPassedToMapState
- @connect((reduxCount) => {
- reduxCountPassedToMapState = reduxCount
- return reduxCount < 2 ? { a: 'a' } : { a: 'b' }
- })
class InnerComponent extends Component {
render() {
return
}
}
+ interface InnerTPropsStateType {
+ a: string
+ }
+ type InnerNoDispatch = {}
+ type InnerOwnerPropsType = { count: number }
+ const ConnectedInner = connect<
+ InnerTPropsStateType,
+ InnerNoDispatch,
+ InnerOwnerPropsType,
+ RootStateType
+ >((reduxCount) => {
+ reduxCountPassedToMapState = reduxCount
+ return reduxCount < 2 ? { a: 'a' } : { a: 'b' }
+ })(InnerComponent)
- class OuterComponent extends Component {
- constructor() {
- super()
+ interface OuterStateType {
+ count: number
+ }
+ class OuterComponent extends Component<{}, OuterStateType> {
+ constructor(props: {}) {
+ super(props)
this.state = { count: 0 }
}
render() {
- return
+ return
}
}
- let outerComponent
+ let outerComponent = React.createRef()
rtl.render(
- (outerComponent = c)} />
+
)
store.dispatch({ type: '' })
store.dispatch({ type: '' })
- outerComponent.setState(({ count }) => ({ count: count + 1 }))
+ outerComponent.current!.setState(({ count }) => ({ count: count + 1 }))
expect(reduxCountPassedToMapState).toEqual(3)
})
it('should ensure top-down updates for consecutive batched updates', () => {
+ type RootStateType = number
+ interface ActionType {
+ type: string
+ }
const INC = 'INC'
- const reducer = (c = 0, { type }) => (type === INC ? c + 1 : c)
+ const reducer = (c: RootStateType = 0, { type }: ActionType) =>
+ type === INC ? c + 1 : c
const store = createStore(reducer)
- let executionOrder = []
+ let executionOrder: string[] = []
let expectedExecutionOrder = [
'parent map',
'parent render',
@@ -3116,8 +3568,17 @@ describe('React', () => {
b: { id: 'b', name: 'Item B' },
c: { id: 'c', name: 'Item C' },
}
+ interface ActionType {
+ type: string
+ }
+ type RootStateType = {
+ [x: string]: { id: string; name: string }
+ }
- const reducer = (state = initialState, action) => {
+ const reducer = (
+ state: RootStateType = initialState,
+ action: ActionType
+ ) => {
switch (action.type) {
case 'DELETE_B': {
const newState = { ...state }
@@ -3130,32 +3591,54 @@ describe('React', () => {
}
const store = createStore(reducer)
-
- const ListItem = ({ name }) => Name: {name}
+ interface PropsType {
+ name: string | undefined
+ }
+ const ListItem = ({ name }: PropsType) => Name: {name}
let thrownError = null
- const listItemMapState = (state, ownProps) => {
+ type ListItemTStatePropsType = { name: string } | undefined
+ type ListItemNoDispatch = {}
+ type ListItemOwnerProps = {
+ id: string
+ }
+
+ const listItemMapState = (
+ state: RootStateType,
+ ownProps: ListItemOwnerProps
+ ) => {
try {
const item = state[ownProps.id]
// If this line executes when item B has been deleted, it will throw an error.
// For this test to succeed, we should never execute mapState for item B after the item
// has been deleted, because the parent should re-render the component out of existence.
- const { name } = item
+ const { name } = item!
return { name }
} catch (e) {
thrownError = e
}
}
- const ConnectedListItem = connect(listItemMapState)(ListItem)
+ const ConnectedListItem = connect<
+ ListItemTStatePropsType,
+ ListItemNoDispatch,
+ ListItemOwnerProps,
+ RootStateType
+ >(listItemMapState)(ListItem)
+
+ interface AppTStatePropsType {
+ itemIds: string[]
+ }
+ type AppNoDispatchType = {}
+ type OwnPropsType = {}
- const appMapState = (state) => {
+ const appMapState = (state: RootStateType) => {
const itemIds = Object.keys(state)
return { itemIds }
}
- function App({ itemIds }) {
+ function App({ itemIds }: AppTStatePropsType) {
const items = itemIds.map((id) => (
))
@@ -3168,7 +3651,12 @@ describe('React', () => {
)
}
- const ConnectedApp = connect(appMapState)(App)
+ const ConnectedApp = connect<
+ AppTStatePropsType,
+ AppNoDispatchType,
+ OwnPropsType,
+ RootStateType
+ >(appMapState)(App)
rtl.render(
diff --git a/yarn.lock b/yarn.lock
index c08da2d54..797000f47 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3381,6 +3381,15 @@ __metadata:
languageName: node
linkType: hard
+"@types/create-react-class@npm:^15.6.3":
+ version: 15.6.3
+ resolution: "@types/create-react-class@npm:15.6.3"
+ dependencies:
+ "@types/react": "*"
+ checksum: 020720b37d031abeaeaa97b33d3fc021ad53754dcc4ce4d723f6ee10587463b7d853d084e76332ff35a275f883413b39975adbfe943926b7dfb97c0005caa5ee
+ languageName: node
+ linkType: hard
+
"@types/eslint-scope@npm:^3.7.0":
version: 3.7.0
resolution: "@types/eslint-scope@npm:3.7.0"
@@ -14503,6 +14512,7 @@ __metadata:
"@testing-library/react": ^12.0.0
"@testing-library/react-hooks": ^3.4.2
"@testing-library/react-native": ^7.1.0
+ "@types/create-react-class": ^15.6.3
"@types/object-assign": ^4.0.30
"@types/react": ^17.0.14
"@types/react-dom": ^17.0.9