Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 532f696

Browse files
committedJul 18, 2021
Consolidate connect implementation
1 parent 75b4bdd commit 532f696

File tree

2 files changed

+384
-426
lines changed

2 files changed

+384
-426
lines changed
 

‎src/components/connect.tsx

Lines changed: 382 additions & 425 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import defaultSelectorFactory, {
1919
MapDispatchToPropsParam,
2020
MergeProps,
2121
MapDispatchToPropsNonObject,
22-
SelectorFactory,
22+
SelectorFactoryOptions,
2323
} from '../connect/selectorFactory'
2424
import defaultMapDispatchToPropsFactories from '../connect/mapDispatchToProps'
2525
import defaultMapStateToPropsFactories from '../connect/mapStateToProps'
@@ -205,152 +205,406 @@ export interface ConnectProps {
205205
store?: Store
206206
}
207207

208-
export interface ConnectAdvancedOptions {
209-
shouldHandleStateChanges?: boolean
210-
forwardRef?: boolean
211-
context?: typeof ReactReduxContext
212-
pure?: boolean
213-
}
214-
215-
function connectAdvanced<S, TProps, TOwnProps, TFactoryOptions = {}>(
216-
/*
217-
selectorFactory is a func that is responsible for returning the selector function used to
218-
compute new props from state, props, and dispatch. For example:
219-
220-
export default connectAdvanced((dispatch, options) => (state, props) => ({
221-
thing: state.things[props.thingId],
222-
saveThing: fields => dispatch(actionCreators.saveThing(props.thingId, fields)),
223-
}))(YourComponent)
224-
225-
Access to dispatch is provided to the factory so selectorFactories can bind actionCreators
226-
outside of their selector as an optimization. Options passed to connectAdvanced are passed to
227-
the selectorFactory, along with displayName and WrappedComponent, as the second argument.
228-
229-
Note that selectorFactory is responsible for all caching/memoization of inbound and outbound
230-
props. Do not use connectAdvanced directly without memoizing results between calls to your
231-
selector, otherwise the Connect component will re-render on every state or props change.
232-
*/
233-
selectorFactory: SelectorFactory<S, TProps, unknown, unknown>,
234-
// options object:
235-
{
236-
// determines whether this HOC subscribes to store changes
237-
shouldHandleStateChanges = true,
208+
function match<T>(
209+
arg: unknown,
210+
factories: ((value: unknown) => T)[],
211+
name: string
212+
): T {
213+
for (let i = factories.length - 1; i >= 0; i--) {
214+
const result = factories[i](arg)
215+
if (result) return result
216+
}
238217

239-
// use React's forwardRef to expose a ref of the wrapped component
240-
forwardRef = false,
218+
return ((dispatch: Dispatch, options: { wrappedComponentName: string }) => {
219+
throw new Error(
220+
`Invalid value of type ${typeof arg} for ${name} argument when connecting component ${
221+
options.wrappedComponentName
222+
}.`
223+
)
224+
}) as any
225+
}
241226

242-
// the context consumer to use
243-
context = ReactReduxContext,
227+
function strictEqual(a: unknown, b: unknown) {
228+
return a === b
229+
}
244230

245-
// additional options are passed through to the selectorFactory
246-
...connectOptions
247-
}: ConnectAdvancedOptions & Partial<TFactoryOptions> = {}
248-
) {
249-
const Context = context
231+
/**
232+
* Infers the type of props that a connector will inject into a component.
233+
*/
234+
export type ConnectedProps<TConnector> =
235+
TConnector extends InferableComponentEnhancerWithProps<
236+
infer TInjectedProps,
237+
any
238+
>
239+
? unknown extends TInjectedProps
240+
? TConnector extends InferableComponentEnhancer<infer TInjectedProps>
241+
? TInjectedProps
242+
: never
243+
: TInjectedProps
244+
: never
250245

251-
type WrappedComponentProps = TOwnProps & ConnectProps
246+
export interface ConnectOptions<
247+
State = DefaultRootState,
248+
TStateProps = {},
249+
TOwnProps = {},
250+
TMergedProps = {}
251+
> {
252+
forwardRef?: boolean
253+
context?: typeof ReactReduxContext
254+
pure?: boolean
255+
areStatesEqual?: (nextState: State, prevState: State) => boolean
252256

253-
const wrapWithConnect: AdvancedComponentDecorator<
254-
TProps,
255-
WrappedComponentProps
256-
> = (WrappedComponent) => {
257-
if (
258-
process.env.NODE_ENV !== 'production' &&
259-
!isValidElementType(WrappedComponent)
260-
) {
261-
throw new Error(
262-
`You must pass a component to the function returned by connect. Instead received ${stringifyComponent(
263-
WrappedComponent
264-
)}`
265-
)
266-
}
257+
areOwnPropsEqual?: (
258+
nextOwnProps: TOwnProps,
259+
prevOwnProps: TOwnProps
260+
) => boolean
267261

268-
const wrappedComponentName =
269-
WrappedComponent.displayName || WrappedComponent.name || 'Component'
262+
areStatePropsEqual?: (
263+
nextStateProps: TStateProps,
264+
prevStateProps: TStateProps
265+
) => boolean
266+
areMergedPropsEqual?: (
267+
nextMergedProps: TMergedProps,
268+
prevMergedProps: TMergedProps
269+
) => boolean
270+
}
270271

271-
const displayName = `Connect(${wrappedComponentName})`
272+
/* @public */
273+
function connect(): InferableComponentEnhancer<DispatchProp>
272274

273-
const selectorFactoryOptions = {
274-
...connectOptions,
275-
shouldHandleStateChanges,
276-
displayName,
277-
wrappedComponentName,
278-
WrappedComponent,
279-
}
275+
/* @public */
276+
function connect<
277+
TStateProps = {},
278+
no_dispatch = {},
279+
TOwnProps = {},
280+
State = DefaultRootState
281+
>(
282+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>
283+
): InferableComponentEnhancerWithProps<TStateProps & DispatchProp, TOwnProps>
280284

281-
const { pure } = connectOptions
285+
/* @public */
286+
function connect<no_state = {}, TDispatchProps = {}, TOwnProps = {}>(
287+
mapStateToProps: null | undefined,
288+
mapDispatchToProps: MapDispatchToPropsNonObject<TDispatchProps, TOwnProps>
289+
): InferableComponentEnhancerWithProps<TDispatchProps, TOwnProps>
282290

283-
// If we aren't running in "pure" mode, we don't want to memoize values.
284-
// To avoid conditionally calling hooks, we fall back to a tiny wrapper
285-
// that just executes the given callback immediately.
286-
const usePureOnlyMemo = pure
287-
? useMemo
288-
: (callback: () => void) => callback()
291+
/* @public */
292+
function connect<no_state = {}, TDispatchProps = {}, TOwnProps = {}>(
293+
mapStateToProps: null | undefined,
294+
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>
295+
): InferableComponentEnhancerWithProps<ResolveThunks<TDispatchProps>, TOwnProps>
289296

290-
function ConnectFunction<TOwnProps>(props: ConnectProps & TOwnProps) {
291-
const [propsContext, reactReduxForwardedRef, wrapperProps] =
292-
useMemo(() => {
293-
// Distinguish between actual "data" props that were passed to the wrapper component,
294-
// and values needed to control behavior (forwarded refs, alternate context instances).
295-
// To maintain the wrapperProps object reference, memoize this destructuring.
296-
const { reactReduxForwardedRef, ...wrapperProps } = props
297-
return [props.context, reactReduxForwardedRef, wrapperProps]
298-
}, [props])
297+
/* @public */
298+
function connect<
299+
TStateProps = {},
300+
TDispatchProps = {},
301+
TOwnProps = {},
302+
State = DefaultRootState
303+
>(
304+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
305+
mapDispatchToProps: MapDispatchToPropsNonObject<TDispatchProps, TOwnProps>
306+
): InferableComponentEnhancerWithProps<TStateProps & TDispatchProps, TOwnProps>
299307

300-
const ContextToUse: ReactReduxContextInstance = useMemo(() => {
301-
// Users may optionally pass in a custom context instance to use instead of our ReactReduxContext.
302-
// Memoize the check that determines which context instance we should use.
303-
return propsContext &&
304-
propsContext.Consumer &&
305-
// @ts-ignore
306-
isContextConsumer(<propsContext.Consumer />)
307-
? propsContext
308-
: Context
309-
}, [propsContext, Context])
308+
/* @public */
309+
function connect<
310+
TStateProps = {},
311+
TDispatchProps = {},
312+
TOwnProps = {},
313+
State = DefaultRootState
314+
>(
315+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
316+
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>
317+
): InferableComponentEnhancerWithProps<
318+
TStateProps & ResolveThunks<TDispatchProps>,
319+
TOwnProps
320+
>
310321

311-
// Retrieve the store and ancestor subscription via context, if available
312-
const contextValue = useContext(ContextToUse)
322+
/* @public */
323+
function connect<
324+
no_state = {},
325+
no_dispatch = {},
326+
TOwnProps = {},
327+
TMergedProps = {}
328+
>(
329+
mapStateToProps: null | undefined,
330+
mapDispatchToProps: null | undefined,
331+
mergeProps: MergeProps<undefined, undefined, TOwnProps, TMergedProps>
332+
): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>
313333

314-
// The store _must_ exist as either a prop or in context.
315-
// We'll check to see if it _looks_ like a Redux store first.
316-
// This allows us to pass through a `store` prop that is just a plain value.
317-
const didStoreComeFromProps =
318-
Boolean(props.store) &&
319-
Boolean(props.store!.getState) &&
320-
Boolean(props.store!.dispatch)
321-
const didStoreComeFromContext =
322-
Boolean(contextValue) && Boolean(contextValue!.store)
334+
/* @public */
335+
function connect<
336+
TStateProps = {},
337+
no_dispatch = {},
338+
TOwnProps = {},
339+
TMergedProps = {},
340+
State = DefaultRootState
341+
>(
342+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
343+
mapDispatchToProps: null | undefined,
344+
mergeProps: MergeProps<TStateProps, undefined, TOwnProps, TMergedProps>
345+
): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>
323346

324-
if (
325-
process.env.NODE_ENV !== 'production' &&
326-
!didStoreComeFromProps &&
327-
!didStoreComeFromContext
328-
) {
329-
throw new Error(
330-
`Could not find "store" in the context of ` +
331-
`"${displayName}". Either wrap the root component in a <Provider>, ` +
332-
`or pass a custom React context provider to <Provider> and the corresponding ` +
333-
`React context consumer to ${displayName} in connect options.`
334-
)
335-
}
347+
/* @public */
348+
function connect<
349+
no_state = {},
350+
TDispatchProps = {},
351+
TOwnProps = {},
352+
TMergedProps = {}
353+
>(
354+
mapStateToProps: null | undefined,
355+
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
356+
mergeProps: MergeProps<undefined, TDispatchProps, TOwnProps, TMergedProps>
357+
): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>
336358

337-
// Based on the previous check, one of these must be true
338-
const store: Store = didStoreComeFromProps
339-
? props.store!
340-
: contextValue!.store
359+
/* @public */
360+
// @ts-ignore
361+
function connect<
362+
TStateProps = {},
363+
no_dispatch = {},
364+
TOwnProps = {},
365+
State = DefaultRootState
366+
>(
367+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
368+
mapDispatchToProps: null | undefined,
369+
mergeProps: null | undefined,
370+
options: ConnectOptions<State, TStateProps, TOwnProps>
371+
): InferableComponentEnhancerWithProps<DispatchProp & TStateProps, TOwnProps>
341372

342-
const childPropsSelector = useMemo(() => {
343-
// The child props selector needs the store reference as an input.
344-
// Re-create this selector whenever the store changes.
345-
return selectorFactory(store.dispatch, selectorFactoryOptions)
346-
}, [store])
373+
/* @public */
374+
function connect<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}>(
375+
mapStateToProps: null | undefined,
376+
mapDispatchToProps: MapDispatchToPropsNonObject<TDispatchProps, TOwnProps>,
377+
mergeProps: null | undefined,
378+
options: ConnectOptions<{}, TStateProps, TOwnProps>
379+
): InferableComponentEnhancerWithProps<TDispatchProps, TOwnProps>
347380

348-
const [subscription, notifyNestedSubs] = useMemo(() => {
349-
if (!shouldHandleStateChanges) return NO_SUBSCRIPTION_ARRAY
381+
/* @public */
382+
function connect<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}>(
383+
mapStateToProps: null | undefined,
384+
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
385+
mergeProps: null | undefined,
386+
options: ConnectOptions<{}, TStateProps, TOwnProps>
387+
): InferableComponentEnhancerWithProps<ResolveThunks<TDispatchProps>, TOwnProps>
350388

351-
// This Subscription's source should match where store came from: props vs. context. A component
352-
// connected to the store via props shouldn't use subscription from context, or vice versa.
353-
const subscription = createSubscription(
389+
/* @public */
390+
function connect<
391+
TStateProps = {},
392+
TDispatchProps = {},
393+
TOwnProps = {},
394+
State = DefaultRootState
395+
>(
396+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
397+
mapDispatchToProps: MapDispatchToPropsNonObject<TDispatchProps, TOwnProps>,
398+
mergeProps: null | undefined,
399+
options: ConnectOptions<State, TStateProps, TOwnProps>
400+
): InferableComponentEnhancerWithProps<TStateProps & TDispatchProps, TOwnProps>
401+
402+
/* @public */
403+
function connect<
404+
TStateProps = {},
405+
TDispatchProps = {},
406+
TOwnProps = {},
407+
State = DefaultRootState
408+
>(
409+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
410+
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
411+
mergeProps: null | undefined,
412+
options: ConnectOptions<State, TStateProps, TOwnProps>
413+
): InferableComponentEnhancerWithProps<
414+
TStateProps & ResolveThunks<TDispatchProps>,
415+
TOwnProps
416+
>
417+
418+
/* @public */
419+
function connect<
420+
TStateProps = {},
421+
TDispatchProps = {},
422+
TOwnProps = {},
423+
TMergedProps = {},
424+
State = DefaultRootState
425+
>(
426+
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
427+
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
428+
mergeProps: MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>,
429+
options?: ConnectOptions<State, TStateProps, TOwnProps, TMergedProps>
430+
): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>
431+
432+
/**
433+
* Connects a React component to a Redux store.
434+
*
435+
* - Without arguments, just wraps the component, without changing the behavior / props
436+
*
437+
* - If 2 params are passed (3rd param, mergeProps, is skipped), default behavior
438+
* is to override ownProps (as stated in the docs), so what remains is everything that's
439+
* not a state or dispatch prop
440+
*
441+
* - When 3rd param is passed, we don't know if ownProps propagate and whether they
442+
* should be valid component props, because it depends on mergeProps implementation.
443+
* As such, it is the user's responsibility to extend ownProps interface from state or
444+
* dispatch props or both when applicable
445+
*
446+
* @param mapStateToProps A function that extracts values from state
447+
* @param mapDispatchToProps Setup for dispatching actions
448+
* @param mergeProps Optional callback to merge state and dispatch props together
449+
* @param options Options for configuring the connection
450+
*
451+
*/
452+
function connect<
453+
TStateProps = {},
454+
TDispatchProps = {},
455+
TOwnProps = {},
456+
TMergedProps = {},
457+
State = DefaultRootState
458+
>(
459+
mapStateToProps?: MapStateToPropsParam<TStateProps, TOwnProps, State>,
460+
mapDispatchToProps?: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
461+
mergeProps?: MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>,
462+
{
463+
pure = true,
464+
areStatesEqual = strictEqual,
465+
areOwnPropsEqual = shallowEqual,
466+
areStatePropsEqual = shallowEqual,
467+
areMergedPropsEqual = shallowEqual,
468+
469+
// use React's forwardRef to expose a ref of the wrapped component
470+
forwardRef = false,
471+
472+
// the context consumer to use
473+
context = ReactReduxContext,
474+
}: ConnectOptions<unknown, unknown, unknown, unknown> = {}
475+
): unknown {
476+
const Context = context
477+
478+
type WrappedComponentProps = TOwnProps & ConnectProps
479+
480+
const initMapStateToProps = match(
481+
mapStateToProps,
482+
// @ts-ignore
483+
defaultMapStateToPropsFactories,
484+
'mapStateToProps'
485+
)!
486+
const initMapDispatchToProps = match(
487+
mapDispatchToProps,
488+
// @ts-ignore
489+
defaultMapDispatchToPropsFactories,
490+
'mapDispatchToProps'
491+
)!
492+
const initMergeProps = match(
493+
mergeProps,
494+
// @ts-ignore
495+
defaultMergePropsFactories,
496+
'mergeProps'
497+
)!
498+
499+
const shouldHandleStateChanges = Boolean(mapStateToProps)
500+
501+
const wrapWithConnect: AdvancedComponentDecorator<
502+
TOwnProps,
503+
WrappedComponentProps
504+
> = (WrappedComponent) => {
505+
if (
506+
process.env.NODE_ENV !== 'production' &&
507+
!isValidElementType(WrappedComponent)
508+
) {
509+
throw new Error(
510+
`You must pass a component to the function returned by connect. Instead received ${stringifyComponent(
511+
WrappedComponent
512+
)}`
513+
)
514+
}
515+
516+
const wrappedComponentName =
517+
WrappedComponent.displayName || WrappedComponent.name || 'Component'
518+
519+
const displayName = `Connect(${wrappedComponentName})`
520+
521+
const selectorFactoryOptions: SelectorFactoryOptions<any, any, any, any> = {
522+
pure,
523+
shouldHandleStateChanges,
524+
displayName,
525+
wrappedComponentName,
526+
WrappedComponent,
527+
initMapStateToProps,
528+
initMapDispatchToProps,
529+
// @ts-ignore
530+
initMergeProps,
531+
areStatesEqual,
532+
areStatePropsEqual,
533+
areOwnPropsEqual,
534+
areMergedPropsEqual,
535+
}
536+
537+
// If we aren't running in "pure" mode, we don't want to memoize values.
538+
// To avoid conditionally calling hooks, we fall back to a tiny wrapper
539+
// that just executes the given callback immediately.
540+
const usePureOnlyMemo = pure
541+
? useMemo
542+
: (callback: () => void) => callback()
543+
544+
function ConnectFunction<TOwnProps>(props: ConnectProps & TOwnProps) {
545+
const [propsContext, reactReduxForwardedRef, wrapperProps] =
546+
useMemo(() => {
547+
// Distinguish between actual "data" props that were passed to the wrapper component,
548+
// and values needed to control behavior (forwarded refs, alternate context instances).
549+
// To maintain the wrapperProps object reference, memoize this destructuring.
550+
const { reactReduxForwardedRef, ...wrapperProps } = props
551+
return [props.context, reactReduxForwardedRef, wrapperProps]
552+
}, [props])
553+
554+
const ContextToUse: ReactReduxContextInstance = useMemo(() => {
555+
// Users may optionally pass in a custom context instance to use instead of our ReactReduxContext.
556+
// Memoize the check that determines which context instance we should use.
557+
return propsContext &&
558+
propsContext.Consumer &&
559+
// @ts-ignore
560+
isContextConsumer(<propsContext.Consumer />)
561+
? propsContext
562+
: Context
563+
}, [propsContext, Context])
564+
565+
// Retrieve the store and ancestor subscription via context, if available
566+
const contextValue = useContext(ContextToUse)
567+
568+
// The store _must_ exist as either a prop or in context.
569+
// We'll check to see if it _looks_ like a Redux store first.
570+
// This allows us to pass through a `store` prop that is just a plain value.
571+
const didStoreComeFromProps =
572+
Boolean(props.store) &&
573+
Boolean(props.store!.getState) &&
574+
Boolean(props.store!.dispatch)
575+
const didStoreComeFromContext =
576+
Boolean(contextValue) && Boolean(contextValue!.store)
577+
578+
if (
579+
process.env.NODE_ENV !== 'production' &&
580+
!didStoreComeFromProps &&
581+
!didStoreComeFromContext
582+
) {
583+
throw new Error(
584+
`Could not find "store" in the context of ` +
585+
`"${displayName}". Either wrap the root component in a <Provider>, ` +
586+
`or pass a custom React context provider to <Provider> and the corresponding ` +
587+
`React context consumer to ${displayName} in connect options.`
588+
)
589+
}
590+
591+
// Based on the previous check, one of these must be true
592+
const store: Store = didStoreComeFromProps
593+
? props.store!
594+
: contextValue!.store
595+
596+
const childPropsSelector = useMemo(() => {
597+
// The child props selector needs the store reference as an input.
598+
// Re-create this selector whenever the store changes.
599+
return defaultSelectorFactory(store.dispatch, selectorFactoryOptions)
600+
}, [store])
601+
602+
const [subscription, notifyNestedSubs] = useMemo(() => {
603+
if (!shouldHandleStateChanges) return NO_SUBSCRIPTION_ARRAY
604+
605+
// This Subscription's source should match where store came from: props vs. context. A component
606+
// connected to the store via props shouldn't use subscription from context, or vice versa.
607+
const subscription = createSubscription(
354608
store,
355609
didStoreComeFromProps ? undefined : contextValue!.subscription
356610
)
@@ -524,301 +778,4 @@ function connectAdvanced<S, TProps, TOwnProps, TFactoryOptions = {}>(
524778
return wrapWithConnect
525779
}
526780

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-
824781
export default connect

‎src/connect/selectorFactory.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ interface PureSelectorFactoryComparisonOptions<
9696
areStatesEqual: EqualityFn<State>
9797
areOwnPropsEqual: EqualityFn<TOwnProps>
9898
areStatePropsEqual: EqualityFn<unknown>
99+
displayName: string
99100
pure?: boolean
100101
}
101102

@@ -222,7 +223,7 @@ export interface SelectorFactoryOptions<
222223
// TODO: Add more comments
223224

224225
// If pure is true, the selector returned by selectorFactory will memoize its results,
225-
// allowing connectAdvanced's shouldComponentUpdate to return false if final
226+
// allowing connect's shouldComponentUpdate to return false if final
226227
// props have not changed. If false, the selector will always return a new
227228
// object and shouldComponentUpdate will always return true.
228229

0 commit comments

Comments
 (0)
Please sign in to comment.