Skip to content

Commit ae97b09

Browse files
committed
Consolidate connect logic into one file
1 parent cb82af2 commit ae97b09

File tree

4 files changed

+329
-346
lines changed

4 files changed

+329
-346
lines changed

src/components/Provider.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ export interface ProviderProps<A extends Action = AnyAction> {
1212
store: Store<FixTypeLater, A>
1313
/**
1414
* Optional context to be used internally in react-redux. Use React.createContext() to create a context to be used.
15-
* If this is used, generate own connect HOC by using connectAdvanced, supplying the same context provided to the
16-
* Provider. Initial value doesn't matter, as it is overwritten with the internal state of Provider.
15+
* If this is used, you'll need to customize `connect` by supplying the same context provided to the Provider.
16+
* Initial value doesn't matter, as it is overwritten with the internal state of Provider.
1717
*/
1818
context?: Context<ReactReduxContextValue>
1919
children: ReactNode

src/components/connectAdvanced.tsx

Lines changed: 323 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,33 @@
1+
/* eslint-disable valid-jsdoc, @typescript-eslint/no-unused-vars */
12
import hoistStatics from 'hoist-non-react-statics'
23
import React, { useContext, useMemo, useRef, useReducer } from 'react'
34
import { isValidElementType, isContextConsumer } from 'react-is'
4-
import type { Store } from 'redux'
5-
import type { SelectorFactory } from '../connect/selectorFactory'
5+
import type { Store, Dispatch, Action, AnyAction } from 'redux'
6+
7+
import type {
8+
AdvancedComponentDecorator,
9+
ConnectedComponent,
10+
DefaultRootState,
11+
InferableComponentEnhancer,
12+
InferableComponentEnhancerWithProps,
13+
ResolveThunks,
14+
DispatchProp,
15+
} from '../types'
16+
17+
import defaultSelectorFactory, {
18+
MapStateToPropsParam,
19+
MapDispatchToPropsParam,
20+
MergeProps,
21+
MapDispatchToPropsNonObject,
22+
SelectorFactory,
23+
} from '../connect/selectorFactory'
24+
import defaultMapDispatchToPropsFactories from '../connect/mapDispatchToProps'
25+
import defaultMapStateToPropsFactories from '../connect/mapStateToProps'
26+
import defaultMergePropsFactories from '../connect/mergeProps'
27+
628
import { createSubscription, Subscription } from '../utils/Subscription'
729
import { useIsomorphicLayoutEffect } from '../utils/useIsomorphicLayoutEffect'
8-
import type { AdvancedComponentDecorator, ConnectedComponent } from '../types'
30+
import shallowEqual from '../utils/shallowEqual'
931

1032
import {
1133
ReactReduxContext,
@@ -502,4 +524,301 @@ function connectAdvanced<S, TProps, TOwnProps, TFactoryOptions = {}>(
502524
return wrapWithConnect
503525
}
504526

505-
export default connectAdvanced
527+
function match<T>(
528+
arg: unknown,
529+
factories: ((value: unknown) => T)[],
530+
name: string
531+
): T {
532+
for (let i = factories.length - 1; i >= 0; i--) {
533+
const result = factories[i](arg)
534+
if (result) return result
535+
}
536+
537+
return ((dispatch: Dispatch, options: { wrappedComponentName: string }) => {
538+
throw new Error(
539+
`Invalid value of type ${typeof arg} for ${name} argument when connecting component ${
540+
options.wrappedComponentName
541+
}.`
542+
)
543+
}) as any
544+
}
545+
546+
function strictEqual(a: unknown, b: unknown) {
547+
return a === b
548+
}
549+
550+
/**
551+
* Infers the type of props that a connector will inject into a component.
552+
*/
553+
export type ConnectedProps<TConnector> =
554+
TConnector extends InferableComponentEnhancerWithProps<
555+
infer TInjectedProps,
556+
any
557+
>
558+
? unknown extends TInjectedProps
559+
? TConnector extends InferableComponentEnhancer<infer TInjectedProps>
560+
? TInjectedProps
561+
: never
562+
: TInjectedProps
563+
: never
564+
565+
export interface ConnectOptions<
566+
State = DefaultRootState,
567+
TStateProps = {},
568+
TOwnProps = {},
569+
TMergedProps = {}
570+
> extends ConnectAdvancedOptions {
571+
pure?: boolean
572+
areStatesEqual?: (nextState: State, prevState: State) => boolean
573+
574+
areOwnPropsEqual?: (
575+
nextOwnProps: TOwnProps,
576+
prevOwnProps: TOwnProps
577+
) => boolean
578+
579+
areStatePropsEqual?: (
580+
nextStateProps: TStateProps,
581+
prevStateProps: TStateProps
582+
) => boolean
583+
areMergedPropsEqual?: (
584+
nextMergedProps: TMergedProps,
585+
prevMergedProps: TMergedProps
586+
) => boolean
587+
forwardRef?: boolean
588+
}
589+
590+
/* @public */
591+
function connect(): InferableComponentEnhancer<DispatchProp>
592+
593+
/* @public */
594+
function connect<
595+
TStateProps = {},
596+
no_dispatch = {},
597+
TOwnProps = {},
598+
State = DefaultRootState
599+
>(
600+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>
601+
): InferableComponentEnhancerWithProps<TStateProps & DispatchProp, TOwnProps>
602+
603+
/* @public */
604+
function connect<no_state = {}, TDispatchProps = {}, TOwnProps = {}>(
605+
mapStateToProps: null | undefined,
606+
mapDispatchToProps: MapDispatchToPropsNonObject<TDispatchProps, TOwnProps>
607+
): InferableComponentEnhancerWithProps<TDispatchProps, TOwnProps>
608+
609+
/* @public */
610+
function connect<no_state = {}, TDispatchProps = {}, TOwnProps = {}>(
611+
mapStateToProps: null | undefined,
612+
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>
613+
): InferableComponentEnhancerWithProps<ResolveThunks<TDispatchProps>, TOwnProps>
614+
615+
/* @public */
616+
function connect<
617+
TStateProps = {},
618+
TDispatchProps = {},
619+
TOwnProps = {},
620+
State = DefaultRootState
621+
>(
622+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
623+
mapDispatchToProps: MapDispatchToPropsNonObject<TDispatchProps, TOwnProps>
624+
): InferableComponentEnhancerWithProps<TStateProps & TDispatchProps, TOwnProps>
625+
626+
/* @public */
627+
function connect<
628+
TStateProps = {},
629+
TDispatchProps = {},
630+
TOwnProps = {},
631+
State = DefaultRootState
632+
>(
633+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
634+
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>
635+
): InferableComponentEnhancerWithProps<
636+
TStateProps & ResolveThunks<TDispatchProps>,
637+
TOwnProps
638+
>
639+
640+
/* @public */
641+
function connect<
642+
no_state = {},
643+
no_dispatch = {},
644+
TOwnProps = {},
645+
TMergedProps = {}
646+
>(
647+
mapStateToProps: null | undefined,
648+
mapDispatchToProps: null | undefined,
649+
mergeProps: MergeProps<undefined, undefined, TOwnProps, TMergedProps>
650+
): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>
651+
652+
/* @public */
653+
function connect<
654+
TStateProps = {},
655+
no_dispatch = {},
656+
TOwnProps = {},
657+
TMergedProps = {},
658+
State = DefaultRootState
659+
>(
660+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
661+
mapDispatchToProps: null | undefined,
662+
mergeProps: MergeProps<TStateProps, undefined, TOwnProps, TMergedProps>
663+
): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>
664+
665+
/* @public */
666+
function connect<
667+
no_state = {},
668+
TDispatchProps = {},
669+
TOwnProps = {},
670+
TMergedProps = {}
671+
>(
672+
mapStateToProps: null | undefined,
673+
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
674+
mergeProps: MergeProps<undefined, TDispatchProps, TOwnProps, TMergedProps>
675+
): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>
676+
677+
/* @public */
678+
// @ts-ignore
679+
function connect<
680+
TStateProps = {},
681+
no_dispatch = {},
682+
TOwnProps = {},
683+
State = DefaultRootState
684+
>(
685+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
686+
mapDispatchToProps: null | undefined,
687+
mergeProps: null | undefined,
688+
options: ConnectOptions<State, TStateProps, TOwnProps>
689+
): InferableComponentEnhancerWithProps<DispatchProp & TStateProps, TOwnProps>
690+
691+
/* @public */
692+
function connect<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}>(
693+
mapStateToProps: null | undefined,
694+
mapDispatchToProps: MapDispatchToPropsNonObject<TDispatchProps, TOwnProps>,
695+
mergeProps: null | undefined,
696+
options: ConnectOptions<{}, TStateProps, TOwnProps>
697+
): InferableComponentEnhancerWithProps<TDispatchProps, TOwnProps>
698+
699+
/* @public */
700+
function connect<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}>(
701+
mapStateToProps: null | undefined,
702+
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
703+
mergeProps: null | undefined,
704+
options: ConnectOptions<{}, TStateProps, TOwnProps>
705+
): InferableComponentEnhancerWithProps<ResolveThunks<TDispatchProps>, TOwnProps>
706+
707+
/* @public */
708+
function connect<
709+
TStateProps = {},
710+
TDispatchProps = {},
711+
TOwnProps = {},
712+
State = DefaultRootState
713+
>(
714+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
715+
mapDispatchToProps: MapDispatchToPropsNonObject<TDispatchProps, TOwnProps>,
716+
mergeProps: null | undefined,
717+
options: ConnectOptions<State, TStateProps, TOwnProps>
718+
): InferableComponentEnhancerWithProps<TStateProps & TDispatchProps, TOwnProps>
719+
720+
/* @public */
721+
function connect<
722+
TStateProps = {},
723+
TDispatchProps = {},
724+
TOwnProps = {},
725+
State = DefaultRootState
726+
>(
727+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
728+
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
729+
mergeProps: null | undefined,
730+
options: ConnectOptions<State, TStateProps, TOwnProps>
731+
): InferableComponentEnhancerWithProps<
732+
TStateProps & ResolveThunks<TDispatchProps>,
733+
TOwnProps
734+
>
735+
736+
/* @public */
737+
function connect<
738+
TStateProps = {},
739+
TDispatchProps = {},
740+
TOwnProps = {},
741+
TMergedProps = {},
742+
State = DefaultRootState
743+
>(
744+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
745+
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
746+
mergeProps: MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>,
747+
options?: ConnectOptions<State, TStateProps, TOwnProps, TMergedProps>
748+
): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>
749+
750+
/**
751+
* Connects a React component to a Redux store.
752+
*
753+
* - Without arguments, just wraps the component, without changing the behavior / props
754+
*
755+
* - If 2 params are passed (3rd param, mergeProps, is skipped), default behavior
756+
* is to override ownProps (as stated in the docs), so what remains is everything that's
757+
* not a state or dispatch prop
758+
*
759+
* - When 3rd param is passed, we don't know if ownProps propagate and whether they
760+
* should be valid component props, because it depends on mergeProps implementation.
761+
* As such, it is the user's responsibility to extend ownProps interface from state or
762+
* dispatch props or both when applicable
763+
*
764+
* @param mapStateToProps A function that extracts values from state
765+
* @param mapDispatchToProps Setup for dispatching actions
766+
* @param mergeProps Optional callback to merge state and dispatch props together
767+
* @param options Options for configuring the connection
768+
*
769+
*/
770+
function connect(
771+
mapStateToProps?: unknown,
772+
mapDispatchToProps?: unknown,
773+
mergeProps?: unknown,
774+
{
775+
pure = true,
776+
areStatesEqual = strictEqual,
777+
areOwnPropsEqual = shallowEqual,
778+
areStatePropsEqual = shallowEqual,
779+
areMergedPropsEqual = shallowEqual,
780+
...extraOptions
781+
}: ConnectOptions<unknown, unknown, unknown, unknown> = {}
782+
): unknown {
783+
const initMapStateToProps = match(
784+
mapStateToProps,
785+
// @ts-ignore
786+
defaultMapStateToPropsFactories,
787+
'mapStateToProps'
788+
)
789+
const initMapDispatchToProps = match(
790+
mapDispatchToProps,
791+
// @ts-ignore
792+
defaultMapDispatchToPropsFactories,
793+
'mapDispatchToProps'
794+
)
795+
const initMergeProps = match(
796+
mergeProps,
797+
// @ts-ignore
798+
defaultMergePropsFactories,
799+
'mergeProps'
800+
)
801+
802+
return connectAdvanced(
803+
defaultSelectorFactory as SelectorFactory<any, any, any, any>,
804+
{
805+
// if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changes
806+
shouldHandleStateChanges: Boolean(mapStateToProps),
807+
808+
// passed through to selectorFactory
809+
initMapStateToProps,
810+
initMapDispatchToProps,
811+
initMergeProps,
812+
pure,
813+
areStatesEqual,
814+
areOwnPropsEqual,
815+
areStatePropsEqual,
816+
areMergedPropsEqual,
817+
818+
// any extra options args can override defaults of connect or connectAdvanced
819+
...extraOptions,
820+
}
821+
)
822+
}
823+
824+
export default connect

0 commit comments

Comments
 (0)