Overview
First, let’s look at an image:

As you can see, the entry file imports these modules:
- Provider
- connectAdvanced
- connect
Let’s examine these modules one by one.
Provider
It mounts the store onto the context, giving child components the ability to access the store.
connectAdvanced
A higher-order function used to generate connect, which references the following modules:
import { Component, createElement } from "react";
import hoistStatics from "hoist-non-react-statics";
import invariant from "invariant";
import { storeShape, subscriptionShape } from "../utils/PropTypes";
import Subscription from "../utils/Subscription";Subscription
First, let’s look at the Subscription module.
createListenerCollection
Implements a publish-subscribe pattern, providing clear, notify, and subscribe methods. These are used to clear the current queue, trigger events, and subscribe to methods, respectively.
Subscription
A subscription class that subscribes to the onStateChange event. It also ensures that if a parent also has an event subscription, the parent’s event is triggered first, followed by the current component’s event, when an event occurs.
makeSelectorStateful(sourceSelector,store)
function makeSelectorStateful(sourceSelector, store) {
// wrap the selector in an object that tracks its results between runs.
const selector = {
run: function runComponentSelector(props) {
try {
const nextProps = sourceSelector(store.getState(), props);
if (nextProps !== selector.props || selector.error) {
selector.shouldComponentUpdate = true;
selector.props = nextProps;
selector.error = null;
}
} catch (error) {
selector.shouldComponentUpdate = true;
selector.error = error;
}
},
};
return selector;
}The selector is used to compute the props for the next state based on state and prop, and then pass them to the connected component. This function is a wrapper around the selector that, in addition to computing props, also records the result of the previous computation.
connectAdvanced(selectorFactory,{options}) (WrappedComponent)
The primary function of connectAdvanced is to return a higher-order component that generates a Connect component, based on the provided selectorFactory and options.
connect
class Connect extends Component {
constructor(props, context) {
super(props, context)
...
this.initSelector()
this.initSubscription()
}
...
initSelector() {
const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)
this.selector = makeSelectorStateful(sourceSelector, this.store)
this.selector.run(this.props)
}
initSubscription() {
if (!shouldHandleStateChanges) return
// parentSub's source should match where store came from: props vs. context. A component
// connected to the store via props shouldn't use subscription from context, or vice versa.
const parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey]
this.subscription = new Subscription(this.store, parentSub, this.onStateChange.bind(this))
// `notifyNestedSubs` is duplicated to handle the case where the component is unmounted in
// the middle of the notification loop, where `this.subscription` will then be null. An
// extra null check every change can be avoided by copying the method onto `this` and then
// replacing it with a no-op on unmount. This can probably be avoided if Subscription's
// listeners logic is changed to not call listeners that have been unsubscribed in the
// middle of the notification loop.
this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription)
}
...
render() {
const selector = this.selector
selector.shouldComponentUpdate = false
if (selector.error) {
throw selector.error
} else {
return createElement(WrappedComponent, this.addExtraProps(selector.props))
}
}
}This component, defined within connectAdvanced, retrieves data from the store via context, initializes a selector containing the initial store using the initSelector method, and subscribes to store changes via the initSubscription method. The render method of this component merely acts as a proxy, passing the props computed by the selector to the wrapped component.
connect
connect imports the following modules:
import connectAdvanced from "../components/connectAdvanced";
import shallowEqual from "../utils/shallowEqual";
import defaultMapDispatchToPropsFactories from "./mapDispatchToProps";
import defaultMapStateToPropsFactories from "./mapStateToProps";
import defaultMergePropsFactories from "./mergeProps";
import defaultSelectorFactory from "./selectorFactory";It simply builds upon connectAdvanced by passing selectorFactory, mapStateToPropsFactories, mapDispatchToProps, and mapDispatchToPropsFactories as options parameters to connectAdvanced.
defaultSelectorFactory
Defines the behavior of the default selector, which computes the props for the next state from the incoming state.
defaultMap xxx To xxx Factories
These modules are used to generate factory functions that map to props methods. Their code takes the following form:
import { bindActionCreators } from "redux";
import { wrapMapToPropsConstant, wrapMapToPropsFunc } from "./wrapMapToProps";
export function whenMapDispatchToPropsIsFunction(mapDispatchToProps) {
return typeof mapDispatchToProps === "function"
? wrapMapToPropsFunc(mapDispatchToProps, "mapDispatchToProps")
: undefined;
}
export function whenMapDispatchToPropsIsMissing(mapDispatchToProps) {
return !mapDispatchToProps
? wrapMapToPropsConstant((dispatch) => ({ dispatch }))
: undefined;
}
export function whenMapDispatchToPropsIsObject(mapDispatchToProps) {
return mapDispatchToProps && typeof mapDispatchToProps === "object"
? wrapMapToPropsConstant((dispatch) =>
bindActionCreators(mapDispatchToProps, dispatch),
)
: undefined;
}As you can see, they primarily reference the wrapMapToProps module.
wrapMapToProps
wrapMapToPropsFunc
This method is a proxy function that wraps the mapToProps function, performing the following tasks:
- Detects whether the
mapToPropsfunction’s invocation depends onprops, which helpsselectorFactorydecide whether to re-invoke it whenpropschange. - During the first invocation, if
mapToPropsreturns another function after execution, it processes that function and uses it as the newmapToPropsfor subsequent calls. - During the first invocation, it validates whether the result of the call is a plain object. This is to warn developers if their
mapToPropsfunction does not return a valid result.
Summary
React-Redux is a library used to bind Redux to React. While it’s possible to use Redux directly within React without React-Redux, it’s overly cumbersome. It would require manually listening for store changes and manually triggering component render methods. React-Redux provides a higher-order component, connect, to handle this for us, and it also includes several optimizations. All we need to do is provide mapDispatchToProps and mapStateToProps to the components that need to listen to the store.
