Skip to content

Commit

Permalink
Merge pull request #1891 from Methuselah96/merge-props
Browse files Browse the repository at this point in the history
  • Loading branch information
markerikson committed Apr 11, 2022
2 parents e00d8af + 19cc9e4 commit f3441c6
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 137 deletions.
56 changes: 9 additions & 47 deletions src/components/connect.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/* eslint-disable valid-jsdoc, @typescript-eslint/no-unused-vars */
import hoistStatics from 'hoist-non-react-statics'
import React, { useContext, useMemo, useRef, useReducer } from 'react'
import React, { useContext, useMemo, useRef } from 'react'
import { isValidElementType, isContextConsumer } from 'react-is'

import type { Store, Dispatch, Action, AnyAction } from 'redux'
import type { Store } from 'redux'

import type {
AdvancedComponentDecorator,
Expand All @@ -21,15 +21,12 @@ import defaultSelectorFactory, {
MapDispatchToPropsNonObject,
SelectorFactoryOptions,
} from '../connect/selectorFactory'
import defaultMapDispatchToPropsFactories from '../connect/mapDispatchToProps'
import defaultMapStateToPropsFactories from '../connect/mapStateToProps'
import defaultMergePropsFactories from '../connect/mergeProps'
import { mapDispatchToPropsFactory } from '../connect/mapDispatchToProps'
import { mapStateToPropsFactory } from '../connect/mapStateToProps'
import { mergePropsFactory } from '../connect/mergeProps'

import { createSubscription, Subscription } from '../utils/Subscription'
import {
useIsomorphicLayoutEffect,
canUseDOM,
} from '../utils/useIsomorphicLayoutEffect'
import { useIsomorphicLayoutEffect } from '../utils/useIsomorphicLayoutEffect'
import shallowEqual from '../utils/shallowEqual'

import {
Expand Down Expand Up @@ -206,25 +203,6 @@ interface InternalConnectProps extends ConnectProps {
reactReduxForwardedRef?: React.ForwardedRef<unknown>
}

function match<T>(
arg: unknown,
factories: ((value: unknown) => T)[],
name: string
): T {
for (let i = factories.length - 1; i >= 0; i--) {
const result = factories[i](arg)
if (result) return result
}

return ((dispatch: Dispatch, options: { wrappedComponentName: string }) => {
throw new Error(
`Invalid value of type ${typeof arg} for ${name} argument when connecting component ${
options.wrappedComponentName
}.`
)
}) as any
}

function strictEqual(a: unknown, b: unknown) {
return a === b
}
Expand Down Expand Up @@ -485,24 +463,9 @@ function connect<

type WrappedComponentProps = TOwnProps & ConnectProps

const initMapStateToProps = match(
mapStateToProps,
// @ts-ignore
defaultMapStateToPropsFactories,
'mapStateToProps'
)!
const initMapDispatchToProps = match(
mapDispatchToProps,
// @ts-ignore
defaultMapDispatchToPropsFactories,
'mapDispatchToProps'
)!
const initMergeProps = match(
mergeProps,
// @ts-ignore
defaultMergePropsFactories,
'mergeProps'
)!
const initMapStateToProps = mapStateToPropsFactory(mapStateToProps)
const initMapDispatchToProps = mapDispatchToPropsFactory(mapDispatchToProps)
const initMergeProps = mergePropsFactory(mergeProps)

const shouldHandleStateChanges = Boolean(mapStateToProps)

Expand Down Expand Up @@ -541,7 +504,6 @@ function connect<
initMapStateToProps,
// @ts-ignore
initMapDispatchToProps,
// @ts-ignore
initMergeProps,
areStatesEqual,
areStatePropsEqual,
Expand Down
14 changes: 14 additions & 0 deletions src/connect/invalidArgFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { Action, Dispatch } from 'redux'

export function createInvalidArgFactory(arg: unknown, name: string) {
return (
dispatch: Dispatch<Action<unknown>>,
options: { readonly wrappedComponentName: string }
) => {
throw new Error(
`Invalid value of type ${typeof arg} for ${name} argument when connecting component ${
options.wrappedComponentName
}.`
)
}
}
45 changes: 17 additions & 28 deletions src/connect/mapDispatchToProps.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,25 @@
import { ActionCreatorsMapObject, Dispatch } from 'redux'
import { FixTypeLater } from '../types'
import type { Action, Dispatch } from 'redux'
import bindActionCreators from '../utils/bindActionCreators'
import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'
import { createInvalidArgFactory } from './invalidArgFactory'
import type { MapDispatchToPropsParam } from './selectorFactory'

export function whenMapDispatchToPropsIsFunction(
mapDispatchToProps: ActionCreatorsMapObject | FixTypeLater
) {
return typeof mapDispatchToProps === 'function'
? wrapMapToPropsFunc(mapDispatchToProps, 'mapDispatchToProps')
: undefined
}

export function whenMapDispatchToPropsIsMissing(mapDispatchToProps: undefined) {
return !mapDispatchToProps
? wrapMapToPropsConstant((dispatch: Dispatch) => ({
dispatch,
}))
: undefined
}

export function whenMapDispatchToPropsIsObject(
mapDispatchToProps: ActionCreatorsMapObject
export function mapDispatchToPropsFactory<TDispatchProps, TOwnProps>(
mapDispatchToProps:
| MapDispatchToPropsParam<TDispatchProps, TOwnProps>
| undefined
) {
return mapDispatchToProps && typeof mapDispatchToProps === 'object'
? wrapMapToPropsConstant((dispatch: Dispatch) =>
? wrapMapToPropsConstant((dispatch: Dispatch<Action<unknown>>) =>
// @ts-ignore
bindActionCreators(mapDispatchToProps, dispatch)
)
: undefined
: !mapDispatchToProps
? wrapMapToPropsConstant((dispatch: Dispatch<Action<unknown>>) => ({
dispatch,
}))
: typeof mapDispatchToProps === 'function'
? // @ts-ignore
wrapMapToPropsFunc(mapDispatchToProps, 'mapDispatchToProps')
: createInvalidArgFactory(mapDispatchToProps, 'mapDispatchToProps')
}

export default [
whenMapDispatchToPropsIsFunction,
whenMapDispatchToPropsIsMissing,
whenMapDispatchToPropsIsObject,
]
27 changes: 12 additions & 15 deletions src/connect/mapStateToProps.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import {
MapToProps,
wrapMapToPropsConstant,
wrapMapToPropsFunc,
} from './wrapMapToProps'
import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps'
import { createInvalidArgFactory } from './invalidArgFactory'
import type { MapStateToPropsParam } from './selectorFactory'

export function whenMapStateToPropsIsFunction(mapStateToProps?: MapToProps) {
return typeof mapStateToProps === 'function'
? wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps')
: undefined
export function mapStateToPropsFactory<TStateProps, TOwnProps, State>(
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>
) {
return !mapStateToProps
? wrapMapToPropsConstant(() => ({}))
: typeof mapStateToProps === 'function'
? // @ts-ignore
wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps')
: createInvalidArgFactory(mapStateToProps, 'mapStateToProps')
}

export function whenMapStateToPropsIsMissing(mapStateToProps?: MapToProps) {
return !mapStateToProps ? wrapMapToPropsConstant(() => ({})) : undefined
}

export default [whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing]
56 changes: 23 additions & 33 deletions src/connect/mergeProps.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
import { Dispatch } from 'redux'
import type { Action, Dispatch } from 'redux'
import verifyPlainObject from '../utils/verifyPlainObject'
import { createInvalidArgFactory } from './invalidArgFactory'
import type { MergeProps } from './selectorFactory'
import type { EqualityFn } from '../types'

type MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps> = (
stateProps: TStateProps,
dispatchProps: TDispatchProps,
ownProps: TOwnProps
) => TMergedProps

export function defaultMergeProps<TStateProps, TDispatchProps, TOwnProps>(
export function defaultMergeProps<
TStateProps,
TDispatchProps,
TOwnProps,
TMergedProps
>(
stateProps: TStateProps,
dispatchProps: TDispatchProps,
ownProps: TOwnProps
) {
): TMergedProps {
// @ts-ignore
return { ...ownProps, ...stateProps, ...dispatchProps }
}

interface InitMergeOptions {
displayName: string
areMergedPropsEqual: (a: any, b: any) => boolean
}

export function wrapMergePropsFunc<
TStateProps,
TDispatchProps,
Expand All @@ -28,8 +26,11 @@ export function wrapMergePropsFunc<
>(
mergeProps: MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>
): (
dispatch: Dispatch,
options: InitMergeOptions
dispatch: Dispatch<Action<unknown>>,
options: {
readonly displayName: string
readonly areMergedPropsEqual: EqualityFn<TMergedProps>
}
) => MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps> {
return function initMergePropsProxy(
dispatch,
Expand Down Expand Up @@ -61,28 +62,17 @@ export function wrapMergePropsFunc<
}
}

export function whenMergePropsIsFunction<
TStateProps,
TDispatchProps,
TOwnProps,
TMergedProps
>(
mergeProps: MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>
) {
return typeof mergeProps === 'function'
? wrapMergePropsFunc(mergeProps)
: undefined
}

export function whenMergePropsIsOmitted<
export function mergePropsFactory<
TStateProps,
TDispatchProps,
TOwnProps,
TMergedProps
>(
mergeProps?: MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>
) {
return !mergeProps ? () => defaultMergeProps : undefined
return !mergeProps
? () => defaultMergeProps
: typeof mergeProps === 'function'
? wrapMergePropsFunc(mergeProps)
: createInvalidArgFactory(mergeProps, 'mergeProps')
}

export default [whenMergePropsIsFunction, whenMergePropsIsOmitted] as const
37 changes: 23 additions & 14 deletions src/connect/selectorFactory.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Dispatch, Action } from 'redux'
import type { ComponentType } from 'react'
import verifySubselectors from './verifySubselectors'
import type { EqualityFn } from '../types'

Expand Down Expand Up @@ -58,10 +59,9 @@ export type MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps> = (
) => TMergedProps

interface PureSelectorFactoryComparisonOptions<TStateProps, TOwnProps, State> {
areStatesEqual: EqualityFn<State>
areOwnPropsEqual: EqualityFn<TOwnProps>
areStatePropsEqual: EqualityFn<TStateProps>
displayName: string
readonly areStatesEqual: EqualityFn<State>
readonly areStatePropsEqual: EqualityFn<TStateProps>
readonly areOwnPropsEqual: EqualityFn<TOwnProps>
}

export function pureFinalPropsSelectorFactory<
Expand Down Expand Up @@ -162,24 +162,33 @@ interface WrappedMapDispatchToProps<TDispatchProps, TOwnProps> {
readonly dependsOnOwnProps: boolean
}

export interface InitOptions<TStateProps, TOwnProps, TMergedProps, State>
extends PureSelectorFactoryComparisonOptions<TStateProps, TOwnProps, State> {
readonly shouldHandleStateChanges: boolean
readonly displayName: string
readonly wrappedComponentName: string
readonly WrappedComponent: ComponentType<TOwnProps>
readonly areMergedPropsEqual: EqualityFn<TMergedProps>
}

export interface SelectorFactoryOptions<
TStateProps,
TOwnProps,
TDispatchProps,
TMergedProps,
State
> extends PureSelectorFactoryComparisonOptions<TStateProps, TOwnProps, State> {
initMapStateToProps: (
dispatch: Dispatch,
options: PureSelectorFactoryComparisonOptions<TStateProps, TOwnProps, State>
> extends InitOptions<TStateProps, TOwnProps, TMergedProps, State> {
readonly initMapStateToProps: (
dispatch: Dispatch<Action<unknown>>,
options: InitOptions<TStateProps, TOwnProps, TMergedProps, State>
) => WrappedMapStateToProps<TStateProps, TOwnProps, State>
initMapDispatchToProps: (
dispatch: Dispatch,
options: PureSelectorFactoryComparisonOptions<TStateProps, TOwnProps, State>
readonly initMapDispatchToProps: (
dispatch: Dispatch<Action<unknown>>,
options: InitOptions<TStateProps, TOwnProps, TMergedProps, State>
) => WrappedMapDispatchToProps<TDispatchProps, TOwnProps>
initMergeProps: (
dispatch: Dispatch,
options: PureSelectorFactoryComparisonOptions<TStateProps, TOwnProps, State>
readonly initMergeProps: (
dispatch: Dispatch<Action<unknown>>,
options: InitOptions<TStateProps, TOwnProps, TMergedProps, State>
) => MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>
}

Expand Down

0 comments on commit f3441c6

Please sign in to comment.