connect<TReduxState, TProps extends UiProps> function

UiFactory<TProps> Function(UiFactory<TProps> ) connect<TReduxState, TProps extends UiProps>({
  1. Map mapStateToProps(
    1. TReduxState state
    )?,
  2. Map mapStateToPropsWithOwnProps(
    1. TReduxState state,
    2. TProps ownProps
    )?,
  3. Map Function(TReduxState state) makeMapStateToProps(
    1. TReduxState initialState,
    2. TProps initialOwnProps
    )?,
  4. Map Function(TReduxState state, TProps ownProps) makeMapStateToPropsWithOwnProps(
    1. TReduxState initialState,
    2. TProps initialOwnProps
    )?,
  5. Map mapDispatchToProps(
    1. Dispatcher dispatch
    )?,
  6. Map mapDispatchToPropsWithOwnProps(
    1. Dispatcher dispatch,
    2. TProps ownProps
    )?,
  7. Map Function(Dispatcher dispatch) makeMapDispatchToProps(
    1. Dispatcher dispatch,
    2. TProps ownProps
    )?,
  8. Map Function(Dispatcher dispatch, TProps ownProps) makeMapDispatchToPropsWithOwnProps(
    1. Dispatcher dispatch,
    2. TProps ownProps
    )?,
  9. Map mergeProps(
    1. TProps stateProps,
    2. TProps dispatchProps,
    3. TProps ownProps
    )?,
  10. bool areStatesEqual(
    1. TReduxState nextState,
    2. TReduxState prevState
    )?,
  11. bool areOwnPropsEqual(
    1. TProps nextProps,
    2. TProps prevProps
    )? = propsOrStateMapsEqual,
  12. bool areStatePropsEqual(
    1. TProps nextProps,
    2. TProps prevProps
    )? = propsOrStateMapsEqual,
  13. bool areMergedPropsEqual(
    1. TProps nextProps,
    2. TProps prevProps
    )? = propsOrStateMapsEqual,
  14. Context? context,
  15. bool pure = true,
  16. bool forwardRef = false,
})

A wrapper around the JS react-redux connect function that supports OverReact components.

NOTE: This should only be used to wrap components that extend from Component2.

Example:

UiFactory<CounterProps> Counter = connect<CounterState, CounterProps>(
  mapStateToProps: (state) => (Counter()
    ..count = state.count
  ),
  mapDispatchToProps: (dispatch) => (Counter()
    ..increment = (() => dispatch(INCREMENT_ACTION()))
  ),
)(_$Counter);

Example:

    Store store1 = Store<CounterState>(counterStateReducer, initialState: new CounterState(count: 0));
    Store store2 = Store<BigCounterState>(bigCounterStateReducer, initialState: new BigCounterState(bigCount: 100));

    UiFactory<CounterProps> Counter = connect<CounterState, CounterProps>(
      mapStateToProps: (state) => (Counter()..count = state.count)
    )(_$Counter);

    UiFactory<CounterProps> BigCounter = connect<BigCounterState, CounterProps>(
      mapStateToProps: (state) => (BigCounter()..count = state.bigCount),
      context: bigCounterContext,
    )(_$Counter);

    react_dom.render(
      Dom.div()(
        (ReduxProvider()..store = store1)(
          (ReduxProvider()
            ..store = store2
            ..context = bigCounterContext
          )(
            Dom.div()(
              Dom.h3()('BigCounter Store2'),
              BigCounter()(
                Dom.h4()('Counter Store1'),
                Counter()(),
              ),
            ),
          ),
        ),
      ), querySelector('#content')
    );
  • pure if true (default), connect performs several equality checks that are used to avoid unnecessary calls to mapStateToProps, mapDispatchToProps, mergeProps, and ultimately to render. These include areStatesEqual, areOwnPropsEqual, areStatePropsEqual, and areMergedPropsEqual. While the defaults are probably appropriate 99% of the time, you may wish to override them with custom implementations for performance or other reasons.

  • forwardRef if true, the ref prop provided to the connected component will be return the wrapped component.

For more info see: https://react-redux.js.org/api/connect#connect

Implementation

UiFactory<TProps> Function(UiFactory<TProps>) connect<TReduxState, TProps extends component_base.UiProps>({
  Map Function(TReduxState state)? mapStateToProps,
  Map Function(TReduxState state, TProps ownProps)? mapStateToPropsWithOwnProps,
  Map Function(TReduxState state) Function(TReduxState initialState, TProps initialOwnProps)? makeMapStateToProps,
  Map Function(TReduxState state, TProps ownProps) Function(TReduxState initialState, TProps initialOwnProps)? makeMapStateToPropsWithOwnProps,
  Map Function(Dispatcher dispatch)? mapDispatchToProps,
  Map Function(Dispatcher dispatch, TProps ownProps)? mapDispatchToPropsWithOwnProps,
  Map Function(Dispatcher dispatch) Function(Dispatcher dispatch, TProps ownProps)? makeMapDispatchToProps,
  Map Function(Dispatcher dispatch, TProps ownProps) Function(Dispatcher dispatch, TProps ownProps)? makeMapDispatchToPropsWithOwnProps,
  Map Function(TProps stateProps, TProps dispatchProps, TProps ownProps)? mergeProps,
  bool Function(TReduxState nextState, TReduxState prevState)? areStatesEqual,
  // Use default parameter values instead of ??= in the function body to allow consumers
  // to specify `null` and fall back to the JS default.
  bool Function(TProps nextProps, TProps prevProps)? areOwnPropsEqual = propsOrStateMapsEqual,
  bool Function(TProps nextProps, TProps prevProps)? areStatePropsEqual = propsOrStateMapsEqual,
  bool Function(TProps nextProps, TProps prevProps)? areMergedPropsEqual = propsOrStateMapsEqual,
  Context? context,
  bool pure = true,
  bool forwardRef = false,
}) {
  List<dynamic> mapStateToPropsCheck = [mapStateToProps, mapStateToPropsWithOwnProps, makeMapStateToProps, makeMapStateToPropsWithOwnProps]..removeWhere((x) => x == null);
  List<dynamic> mapDispatchToPropsCheck = [mapDispatchToProps, mapDispatchToPropsWithOwnProps, makeMapDispatchToProps, makeMapDispatchToPropsWithOwnProps]..removeWhere((x) => x == null);

  if (mapStateToPropsCheck.length > 1) {
    throw ArgumentError('Only one of the following arguments can be provided at a time: [mapStateToProps, mapStateToPropsWithOwnProps, makeMapStateToProps, makeMapStateToPropsWithOwnProps]');
  }

  if (mapDispatchToPropsCheck.length > 1) {
    throw ArgumentError('Only one of the following arguments can be provided at a time: [mapDispatchToProps, mapDispatchToPropsWithOwnProps, makeMapDispatchToProps, makeMapDispatchToPropsWithOwnProps]');
  }

  UiFactory<TProps> wrapWithConnect(UiFactory<TProps> factory) {
    // `.componentFactory` will only be null for factories that can't be invoked, such as
    // props map views or props for abstract components.
    // Attempting to render one of those would fail, so we'll fail here as well if it gets wrapped.
    final dartComponentFactory = factory().componentFactory!;
    final dartComponentClass = dartComponentFactory.type;
    enforceMinimumComponentVersionFor(dartComponentFactory);

    JsMap jsMapFromProps(Map props) => jsBackingMapOrJsCopy(props is component_base.UiProps ? props.props : props);

    TProps jsPropsToTProps(JsMap jsProps) => factory(JsBackedMap.backedBy(jsProps));

    T allowInteropWithArgCount<T extends Function>(T dartFunction, int count) {
      var interopFunction = allowInterop(dartFunction);
      _defineProperty(interopFunction, 'length', _JsPropertyDescriptor(value: count));
      return interopFunction;
    }

    JsMap handleMapStateToProps(_JsStateValue jsState) {
      return jsMapFromProps(
        mapStateToProps!(
          DartValueWrapper.unwrapIfNeeded(jsState),
        ),
      );
    }

    JsMap handleMapStateToPropsWithOwnProps(_JsStateValue jsState, JsMap jsOwnProps) {
      return jsMapFromProps(
        mapStateToPropsWithOwnProps!(
          DartValueWrapper.unwrapIfNeeded(jsState),
          jsPropsToTProps(jsOwnProps),
        ),
      );
    }

    JsMap Function(_JsStateValue jsState) handleMakeMapStateToProps(_JsStateValue initialJsState, JsMap initialJsOwnProps) {
      var mapToFactory = makeMapStateToProps!(
        DartValueWrapper.unwrapIfNeeded(initialJsState),
        jsPropsToTProps(initialJsOwnProps)
      );
      JsMap handleMakeMapStateToPropsFactory(_JsStateValue jsState) {
        return jsMapFromProps(
          mapToFactory(
            DartValueWrapper.unwrapIfNeeded(jsState),
          ),
        );
      }
      return allowInteropWithArgCount(handleMakeMapStateToPropsFactory, 1);
    }

    JsMap Function(_JsStateValue jsState, JsMap jsOwnProps) handleMakeMapStateToPropsWithOwnProps(_JsStateValue initialJsState, JsMap initialJsOwnProps) {
      var mapToFactory = makeMapStateToPropsWithOwnProps!(
        DartValueWrapper.unwrapIfNeeded(initialJsState),
        jsPropsToTProps(initialJsOwnProps)
      );
      JsMap handleMakeMapStateToPropsWithOwnPropsFactory(_JsStateValue jsState, JsMap jsOwnProps) {
        return jsMapFromProps(
          mapToFactory(
            DartValueWrapper.unwrapIfNeeded(jsState),
            jsPropsToTProps(jsOwnProps),
          ),
        );
      }
      return allowInteropWithArgCount(handleMakeMapStateToPropsWithOwnPropsFactory, 2);
    }

    JsMap handleMapDispatchToProps(Dispatcher dispatch) {
      return jsMapFromProps(
        mapDispatchToProps!(dispatch),
      );
    }

    JsMap handleMapDispatchToPropsWithOwnProps(Dispatcher dispatch, JsMap jsOwnProps) {
      return jsMapFromProps(
        mapDispatchToPropsWithOwnProps!(
          dispatch,
          jsPropsToTProps(jsOwnProps),
        )
      );
    }

    JsMap Function(Dispatcher dispatch) handleMakeMapDispatchToProps(Dispatcher dispatch, JsMap initialJsOwnProps) {
      var mapToFactory = makeMapDispatchToProps!(
        dispatch,
        jsPropsToTProps(initialJsOwnProps)
      );
      JsMap handleMakeMapDispatchToPropsFactory(Dispatcher dispatch) {
        return jsMapFromProps(
          mapToFactory(
            dispatch,
          ),
        );
      }
      return allowInteropWithArgCount(handleMakeMapDispatchToPropsFactory, 1);
    }

    JsMap Function(Dispatcher dispatch, JsMap jsOwnProps) handleMakeMapDispatchToPropsWithOwnProps(Dispatcher dispatch, JsMap initialJsOwnProps) {
      var mapToFactory = makeMapDispatchToPropsWithOwnProps!(
        dispatch,
        jsPropsToTProps(initialJsOwnProps)
      );
      JsMap handleMakeMapDispatchToPropsWithOwnPropsFactory(Dispatcher dispatch, JsMap jsOwnProps) {
        return jsMapFromProps(
          mapToFactory(
            dispatch,
            jsPropsToTProps(jsOwnProps),
          ),
        );
      }
      return allowInteropWithArgCount(handleMakeMapDispatchToPropsWithOwnPropsFactory, 2);
    }

    JsMap handleMergeProps(JsMap jsStateProps, JsMap jsDispatchProps, JsMap jsOwnProps) {
      return jsMapFromProps(
        mergeProps!(
          jsPropsToTProps(jsStateProps),
          jsPropsToTProps(jsDispatchProps),
          jsPropsToTProps(jsOwnProps)
        )
      );
    }

    bool handleAreStatesEqual(_JsStateValue jsNext, _JsStateValue jsPrev) =>
        areStatesEqual!(DartValueWrapper.unwrapIfNeeded(jsNext), DartValueWrapper.unwrapIfNeeded(jsPrev));

    bool handleAreOwnPropsEqual(JsMap jsNext, JsMap jsPrev) =>
        areOwnPropsEqual!(jsPropsToTProps(jsNext), jsPropsToTProps(jsPrev));

    bool handleAreStatePropsEqual(JsMap jsNext, JsMap jsPrev) =>
        areStatePropsEqual!(jsPropsToTProps(jsNext), jsPropsToTProps(jsPrev));

    bool handleAreMergedPropsEqual(JsMap jsNext, JsMap jsPrev) =>
        areMergedPropsEqual!(jsPropsToTProps(jsNext), jsPropsToTProps(jsPrev));

    final connectOptions = JsConnectOptions(
      forwardRef: forwardRef,
      pure: pure,
      context: context?.jsThis ?? JsReactRedux.ReactReduxContext,
    );
    // These can't be `null` in the JS object, so we conditionally define them
    // so they won't exist in the object if we don't want to specify them.
    if (areStatesEqual != null) {
      connectOptions.areStatesEqual = allowInterop(handleAreStatesEqual);
    }
    if (areOwnPropsEqual != null) {
      connectOptions.areOwnPropsEqual = allowInterop(handleAreOwnPropsEqual);
    }
    if (areStatePropsEqual != null) {
      connectOptions.areStatePropsEqual = allowInterop(handleAreStatePropsEqual);
    }
    if (areMergedPropsEqual != null) {
      connectOptions.areMergedPropsEqual = allowInterop(handleAreMergedPropsEqual);
    }

    // return typed as dynamic in case we ever want to allow for the object based syntax
    Function? interopMapStateToPropsHandler() {
      if (mapStateToProps != null) {
        return allowInteropWithArgCount(handleMapStateToProps, 1);
      }
      if (mapStateToPropsWithOwnProps != null) {
        return allowInteropWithArgCount(handleMapStateToPropsWithOwnProps, 2);
      }
      if (makeMapStateToProps != null) {
        return allowInteropWithArgCount(handleMakeMapStateToProps, 2);
      }
      if (makeMapStateToPropsWithOwnProps != null) {
        return allowInteropWithArgCount(handleMakeMapStateToPropsWithOwnProps, 2);
      }
      return null;
    }

    // return typed as dynamic in case we ever want to allow for the object based syntax
    Function? interopMapDispatchToPropsHandler() {
      if (mapDispatchToProps != null) {
        return allowInteropWithArgCount(handleMapDispatchToProps, 1);
      }
      if (mapDispatchToPropsWithOwnProps != null) {
        return allowInteropWithArgCount(handleMapDispatchToPropsWithOwnProps, 2);
      }
      if (makeMapDispatchToProps != null) {
        return allowInteropWithArgCount(handleMakeMapDispatchToProps, 2);
      }
      if (makeMapDispatchToPropsWithOwnProps != null) {
        return allowInteropWithArgCount(handleMakeMapDispatchToPropsWithOwnProps, 2);
      }
      return null;
    }

    final hoc = mockableJsConnect(
      interopMapStateToPropsHandler(),
      interopMapDispatchToPropsHandler(),
      mergeProps != null ? allowInterop(handleMergeProps) : null,
      connectOptions,
    )(dartComponentClass as ReactClass);

    /// Use a Dart proxy instead of a JS one since we're treating it like a Dart component:
    /// props values should be passed to the underlying component (e.g., those returned by mapStateToProps)
    /// without any conversion needed by JS Components, and props are are fed directly
    /// into Dart code (e.g., those passed into mapStateToPropsWithOwnProps/areOwnPropsEqual)
    /// without needing unwrapping/conversion.
    final hocFactoryProxy = ReactDartComponentFactoryProxy2(hoc);
    setComponentTypeMeta(hocFactoryProxy.type, isHoc: true, parentType: dartComponentFactory.type);

    TProps connectedFactory([Map? props]) {
      return (factory(props)..componentFactory = hocFactoryProxy);
    }

    return connectedFactory;
  }

  return wrapWithConnect;
}