|
| 1 | +/* eslint-disable valid-jsdoc, @typescript-eslint/no-unused-vars */ |
1 | 2 | import hoistStatics from 'hoist-non-react-statics'
|
2 | 3 | import React, { useContext, useMemo, useRef, useReducer } from 'react'
|
3 | 4 | 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 | + |
6 | 28 | import { createSubscription, Subscription } from '../utils/Subscription'
|
7 | 29 | import { useIsomorphicLayoutEffect } from '../utils/useIsomorphicLayoutEffect'
|
8 |
| -import type { AdvancedComponentDecorator, ConnectedComponent } from '../types' |
| 30 | +import shallowEqual from '../utils/shallowEqual' |
9 | 31 |
|
10 | 32 | import {
|
11 | 33 | ReactReduxContext,
|
@@ -502,4 +524,301 @@ function connectAdvanced<S, TProps, TOwnProps, TFactoryOptions = {}>(
|
502 | 524 | return wrapWithConnect
|
503 | 525 | }
|
504 | 526 |
|
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