connect<TReduxState, TProps extends UiProps> function
- Map mapStateToProps(
- TReduxState state
- Map mapStateToPropsWithOwnProps(
- TReduxState state,
- TProps ownProps
- Map Function(TReduxState state) makeMapStateToProps(
- TReduxState initialState,
- TProps initialOwnProps
- Map Function(TReduxState state, TProps ownProps) makeMapStateToPropsWithOwnProps(
- TReduxState initialState,
- TProps initialOwnProps
- Map mapDispatchToProps(
- Dispatcher dispatch
- Map mapDispatchToPropsWithOwnProps(
- Dispatcher dispatch,
- TProps ownProps
- Map Function(Dispatcher dispatch) makeMapDispatchToProps(
- Dispatcher dispatch,
- TProps ownProps
- Map Function(Dispatcher dispatch, TProps ownProps) makeMapDispatchToPropsWithOwnProps(
- Dispatcher dispatch,
- TProps ownProps
- Map mergeProps(
- TProps stateProps,
- TProps dispatchProps,
- TProps ownProps
- bool areStatesEqual(
- TReduxState nextState,
- TReduxState prevState
- bool areOwnPropsEqual(
- TProps nextProps,
- TProps prevProps
- bool areStatePropsEqual(
- TProps nextProps,
- TProps prevProps
- bool areMergedPropsEqual(
- TProps nextProps,
- TProps prevProps
- Context? context,
- bool pure = true,
- 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);
-
mapStateToProps
is used for selecting the part of the data from the store that the connected component needs. -
It is called every time the store state changes.
-
It receives the entire store state, and should return an object of data this component needs.
-
If you need access to the props provided to the connected component you can use
mapStateToPropsWithOwnProps
, the second argument will beownProps
. See: react-redux.js.org/using-react-redux/connect-mapstate#defining-mapstatetoprops -
If you need component-instance-specific initialization, such as to setup instance based selectors with memoization, you can use
makeMapStateToProps
ormakeMapStateToPropsWithOwnProps
as a factory function, they will be called once when the component instantiates, and their returns will be used as the actualmapStateToProps
. See: redux.js.org/recipes/computing-derived-data#sharing-selectors-across-multiple-components See: react-redux.js.org/api/connect#factory-functions -
mapDispatchToProps
will be called with dispatch as the first argument.- You will normally make use of this by returning new functions that call dispatch() inside themselves, and either pass in a plain action directly or pass in the result of an action creator.
- If you need access to the props provided to the connected component you can use
mapDispatchToPropsWithOwnProps
, the second argument will beownProps
. See: react-redux.js.org/using-react-redux/connect-mapdispatch#defining-mapdispatchtoprops-as-a-function - If you need component-instance-specific initialization, such as to setup instance based selectors with memoization,
you can use
makeMapDispatchToProps
ormakeMapDispatchToPropsWithOwnProps
as a factory function, they will be called once when the component instantiates, and their returns will be used as the actualmapDispatchToProps
. See: redux.js.org/recipes/computing-derived-data#sharing-selectors-across-multiple-components See: react-redux.js.org/api/connect#factory-functions
-
mergeProps
if specified, defines how the final props for the wrapped component are determined. If you do not providemergeProps
, the wrapped component receives {...ownProps, ...stateProps, ...dispatchProps} by default. -
areStatesEqual
does an equality check using JS===
(equivalent to identical) by default. -
areOwnPropsEqual
does a shallow Map equality check using propsOrStateMapsEqual by default. -
areStatePropsEqual
does a shallow Map equality check using propsOrStateMapsEqual by default. -
areMergedPropsEqual
does a shallow Map equality check using propsOrStateMapsEqual by default. -
context
can be utilized to provide a custom context object created withcreateContext
.context
is how you can utilize multiple stores. While supported, this is not recommended. :P See: redux.js.org/api/store#a-note-for-flux-users See: stackoverflow.com/questions/33619775/redux-multiple-stores-why-not
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
iftrue
(default), connect performs several equality checks that are used to avoid unnecessary calls tomapStateToProps
,mapDispatchToProps
,mergeProps
, and ultimately torender
. These includeareStatesEqual
,areOwnPropsEqual
,areStatePropsEqual
, andareMergedPropsEqual
. 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
iftrue
, theref
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;
}