As a beginner, redux feels so magical when he first gets to know it, no matter what it is, he can just see the API, until he gets to know the higher-order component (HOC). 2 meters? 3 m? I haven’t measured it with a ruler. Until now, I realize that the connect I have been using is a higher-order component, so how to realize this connect, regardless of it, can use ah, right? At that time, the senior technical expert’s keyboard CTRL + C, CTRL + V three keys I used that called a perfect…

Later in the interview the interviewer asked: Have you ever used Redux? Ll: That’s easy. How is CONNECT implemented? After all, CTRL + C + V… Dejected gun, do not take a cloud ~~~ alas, ape road difficult to walk ah

Without further discussion, let’s take a look at some of the main API implementations in React-Redux, based on version 7.2.1

The core mechanism of React-Redux is Subscription and notification. There is a Subscription class that subscribs to parent updates and notifies children of updates. The outermost Provider component Context contains the Store and the generated Subscription instance that subscribes to Redux’s Subscrib (). When we use connect(), it generates a new component, which generates a Subscription instance. It subscribes to the parent’s Subscription and overwrites its own Subscription into the Context. Rewrap the components we pass in

// overriddenContextValue contains the Subscription instance of the new component and the Store < wrapComponent.provider value={overriddenContextValue}> {WrappedComponent} </WrapComponent.Provider>Copy the code

Source analysis

React-redux exports the following API for use

export {
  Provider,
  connectAdvanced,
  ReactReduxContext,
  connect,
  batch,
  useDispatch,
  createDispatchHook,
  useSelector,
  createSelectorHook,
  useStore,
  createStoreHook,
  shallowEqual
}
Copy the code

Subscription

Const nullListeners = {notify() {}} const listeners = {notify() {}} All of our Listeners listen on the current object, and once the current object calls notify, all of our Listeners are executed. Function createListenerCollection() {const Batch = getBatch() let first = const batch = getBatch() let first = null let last = null return { clear() { first = null last = null }, // Notify () {batch(() => {let listener = first while (listener) {listener.callback() listener = listener.next} })}, Get () {let listeners = [] let listeners = first while (listener) {listeners listener.next } return listeners }, Subscribed = true // Subscribe (callback) {let isSubscribed = true // Assign last to a new let listener = (last = {callback, next: null, prev: Last}) // If the previous one exists, If (listener.prev) {listener.prev.next = listener} else {// Otherwise it is the first first = listener} return  function unsubscribe() { if (! isSubscribed || first === null) return isSubscribed = false if (listener.next) { listener.next.prev = listener.prev } else { last = listener.prev } if (listener.prev) { listener.prev.next = listener.next } else { first = listener.next } } }} // Redux can be used as a tree of listeners and can be used as a tree of listeners If our handleChangeWrapper function (which internally executes onStateChange) calls notifyNestedSub, All low-level Subscription instances will be updated. The subsubscription instance's handleChangeWrapper function is executed // this is a top-down event passing mechanism, Export default class Subscription {// Store is Redux's data center constructor(store, constructor) parentSub) { this.store = store this.parentSub = parentSub this.unsubscribe = null this.listeners = nullListeners Enclosing handleChangeWrapper = this. HandleChangeWrapper. Bind (this)} / / addNestedSub Subscription instance as another Subscription This function registers the child Subscription's handleChangeWrapper function with the parent Subscription's parent Listeners All subsubscription handleChangeWrapper functions are executed when the Subscription instance calls notifyNestedSub. AddNestedSub (listener) {// First execute trySubscribe, ParentSub or store this.trysubscribe () // Sublisteners are managed on this.listeners Listeners} notifyNestedSubs() {this.listeners. Subscribe (listener)} notifyNestedSubs() {this.listeners. Notify ()} handleChangeWrapper() {// If (this.onStatechange) {this.onStatechange ()}} isSubscribed() {return Boolean(this.unsubscribe)} trySubscribe() {// If there is no unsubscribe attribute, the next step is calculated based on the parentSub attribute. // If parentSub is not available, store. Subscribe will be used to subscribe to store updates. The handleChangeWrapper function of the modified class is executed. // If parentSub is present, the parentSub addNestedSub function is executed because it exists on the Subscription class, So you can assume that parentSub is an instance of Subscription if (! this.unsubscribe) { this.unsubscribe = this.parentSub ? Enclosing parentSub. AddNestedSub (enclosing handleChangeWrapper) / / subscribe is the method in the story, the story the state change of time is called: this.store.subscribe(this.handleChangeWrapper) this.listeners = createListenerCollection() } } tryUnsubscribe() { if (this.unsubscribe) { this.unsubscribe() this.unsubscribe = null this.listeners.clear() this.listeners = nullListeners } }}Copy the code

Attach a subscription chart

Provider

function Provider({ store, context, Children}) {contextValue const contextValue = useMemo(() => {// Instantiate the subscriber const Subscription = new subscription (store) / / subscribe to the subscription notice child refresh subscription. The onStateChange = Subscription. NotifyNestedSubs return {/ / redux store store, / / the subscription into the context and convenient child subscription subscription}}, Const previousState = useMemo(() => store.getState(), [store]) In contextValue alone, UseEffect (() => {const {subscription} = contextValue // subscribe to reudx store Registered listeners subscription. TrySubscribe () / / if the store state change, notify the child to monitor the if (previousState! . = = store getState ()) {subscription. NotifyNestedSubs ()} / / reset after unloading the return () = > {subscription. TryUnsubscribe () subscription.onStateChange = null } }, [contextValue, PreviousState]) / / incoming context or react to own context const context = context | | ReactReduxContext return <Context.Provider value={contextValue}>{children}</Context.Provider> }Copy the code

A Provider is a simple component that does two things:

  • Subscribe redux’s Subscribe () event

  • Passing the Subscription instance into the Context facilitates child subscriptions

connect

The connect function comes from the createConnect function call. Why is the createConnect function needed? It simply generates the connect function with the default parameters.

There is a comment in the source code that explains the relationship between connect and connectHOC very clearly. You can check it out. Connect passes its ARgs as an option to connectAdvanced (connectHOC), and each time a Connect component instance is instantiated or hot-loaded, they in turn pass them to selectorFactory.

// The first argument we passed is mapStateToProps, Function match(arg, factories, function match(arg, factories, name) { for (let i = factories.length - 1; i >= 0; i--) { const result = factories[i](arg) if (result) return result } return ... (Perform verification)}... Export function createConnect({// Default value, Then introduced connectHOC = connectAdvanced, mapStateToPropsFactories = defaultMapStateToPropsFactories, mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories, mergePropsFactories = defaultMergePropsFactories, selectorFactory = defaultSelectorFactory } = {}) { return function connect( mapStateToProps, mapDispatchToProps, mergeProps, { pure = true, areStatesEqual = strictEqual, areOwnPropsEqual = shallowEqual, areStatePropsEqual = shallowEqual, areMergedPropsEqual = shallowEqual, ... extraOptions } = {} ) { const initMapStateToProps = match( mapStateToProps, mapStateToPropsFactories, 'mapStateToProps' ) const initMapDispatchToProps = match( mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps' ) const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps') return connectHOC(selectorFactory, { // used in error messages methodName: 'connect', // used to compute Connect's displayName from the wrapped component's displayName. getDisplayName: name => `Connect(${name})`, // if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changes shouldHandleStateChanges: Boolean(mapStateToProps), // passed through to selectorFactory initMapStateToProps, initMapDispatchToProps, initMergeProps, pure, areStatesEqual, areOwnPropsEqual, areStatePropsEqual, areMergedPropsEqual, // any extra options args can override defaults of connect or connectAdvanced ... extraOptions }) } } export default /*#__PURE__*/ createConnect()Copy the code

As you can see, the connect function is just a proxy for the connectAdvanced function, and all it does is convert the parameters we pass into parameters that can be used by the selectorFactory. The standard structure that can be used by selectorFactory conforms to the following definition

(dispatch, options) => (nextState, nextOwnProps) => nextFinalProps
Copy the code

As you can see, mapStateToProps, mapDispatchToProps, and mergeProps are actually wrapped with a match() function

mapStateToPropsFactories

/ / when introduced into mapStateToProps, return after wrapMapToPropsFunc packaging export function whenMapStateToPropsIsFunction (mapStateToProps) { return typeof mapStateToProps === 'function' ? wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps') : Undefined} // If mapStateToProps is not passed, Return to a default value after wrapMapToPropsFunc packaging export function whenMapStateToPropsIsMissing (mapStateToProps) {return! mapStateToProps ? wrapMapToPropsConstant(() => ({})) : undefined } export default [whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing]Copy the code

If we pass in mapStateToProps, we call wrapMapToPropsConstant to create a default method. If passed in, we’ll call wrapMapToPropsFunc to wrap up our method, so let’s take a closer look at wrapMapToPropsConstant and wrapMapToPropsFunc

wrapMapToPropsConstant

export function wrapMapToPropsConstant(getConstant) {
  return function initConstantSelector(dispatch, options) {
    const constant = getConstant(dispatch, options)

    function constantSelector() {
      return constant
    }
    constantSelector.dependsOnOwnProps = false
    return constantSelector
  }
}
Copy the code

WrapMapToPropsConstant normalizes the result and returns a constant selector with a dependsOnOwnProps attribute of Fasle, Because we didn’t pass in the corresponding parameters, we didn’t rely on ownProps. The result is a undefined object, because in this case getConstant is an empty function: () => {}

wrapMapToPropsFunc

// This function is used to calculate whether the mapToProps needs to be used // function. Length == 1, function. If the function length! If == 1, this means that props is needed for calculation. / / when mapToProps. DependsOnOwnProps has value when they use this value directly as a result, no longer recount / / if there is still no value, needs a / / calculation logic is mapToProps function parameter format, The number of parameters we pass to the mapStateToProps function, and the ownProps parameter is passed only if the number of parameters is 1. export function getDependsOnOwnProps(mapToProps) { return mapToProps.dependsOnOwnProps ! == null && mapToProps.dependsOnOwnProps ! == undefined ? Boolean(mapToProps.dependsOnOwnProps) : mapToProps.length ! == 1 } export function wrapMapToPropsFunc(mapToProps, methodName) { return function initProxySelector(dispatch, { displayName }) { const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) { return proxy.dependsOnOwnProps ? proxy.mapToProps(stateOrDispatch, ownProps) : MapToProps (stateOrDispatch)} $dependsOnOwnProps () dependsOnOwnProps (); DependsOnOwnProps = true // The detectFactoryAndVerify function is implemented with dependsOnOwnProps DependsOnOwnProps is not re-evaluated when proxy.mapToProps is run a second time. proxy.mapToProps = function detectFactoryAndVerify( stateOrDispatch, {// Assign proxy.mapToProps = mapToProps dependsOnOwnProps = proxy.dependsOnOwnProps = GetDependsOnOwnProps (mapToProps) nextFinalProps let props = proxy(stateOrDispatch, OwnProps) // If the result returned by our mapStateToProps is a function, then in the subsequent calculation, This function is used as the real mapToProps function to evaluate props. This means that the passed mapToProps can return a function as well as a pure object. if (typeof props === 'function') { proxy.mapToProps = props proxy.dependsOnOwnProps = getDependsOnOwnProps(props) props = proxy(stateOrDispatch, ownProps)} // Check the returned props if (process.env.node_env! == 'production') { verifyPlainObject(props, displayName, methodName) } return props } return proxy } }Copy the code

mapDispatchToPropsFactories

import { bindActionCreators } from 'redux' import { wrapMapToPropsConstant, WrapMapToPropsFunc} from './wrapMapToProps' export function whenMapDispatchToPropsIsFunction(mapDispatchToProps) { return typeof mapDispatchToProps === 'function' ? wrapMapToPropsFunc(mapDispatchToProps, 'mapDispatchToProps') : The situation of the undefined} / / mapDispatchToProps null export function whenMapDispatchToPropsIsMissing (mapDispatchToProps) { return ! mapDispatchToProps ? wrapMapToPropsConstant(dispatch => ({ dispatch })) : Undefined} / / mapDispatchToProps as the object of the export function whenMapDispatchToPropsIsObject (mapDispatchToProps) { return mapDispatchToProps && typeof mapDispatchToProps === 'object' ? wrapMapToPropsConstant(dispatch => bindActionCreators(mapDispatchToProps, dispatch) ) : undefined } export default [ whenMapDispatchToPropsIsFunction, whenMapDispatchToPropsIsMissing, whenMapDispatchToPropsIsObject ]Copy the code

There are three functions that handle when the mapDispatchToProps passed in is a function, null, and an object. When mapDispatchToProps is passed as a function, wrapMapToPropsFunc is also called to calculate the result, which is consistent with initMapStateToProps. When mapDispatchToProps is null, the logic is the same as initMapStateToProps, except that the argument is not an empty function. Instead, it is a function that returns an object, which by default contains the dispatch function. This allows us to internally access the Store dispatch API using react-redux via this.props. Dispatch. When mapDispatchToProps is passed as an object, this is an ActionCreator object, You can convert this ActionCreator into an object containing many functions and merge it into props by using redux’s bindActionCreator API.

mergePropsFactories

import verifyPlainObject from '.. /utils/verifyPlainObject' // Merge stateProps, dispatchProps, and ownProps directly and return export function defaultMergeProps(stateProps, dispatchProps, ownProps) { return { ... ownProps, ... stateProps, ... DispatchProps}} // This is a rare case, Export function wrapMergePropsFunc(mergeProps) {return function wrapMergeprops (mergeProps) {return function initMergePropsProxy( dispatch, { displayName, pure, areMergedPropsEqual } ) { let hasRunOnce = false let mergedProps return function mergePropsProxy(stateProps, dispatchProps, ownProps) { const nextMergedProps = mergeProps(stateProps, dispatchProps, ownProps) if (hasRunOnce) { if (! pure || ! areMergedPropsEqual(nextMergedProps, mergedProps)) mergedProps = nextMergedProps } else { hasRunOnce = true mergedProps = nextMergedProps if (process.env.NODE_ENV ! == 'production') verifyPlainObject(mergedProps, displayName, 'mergeProps')} return mergeProps}} export function whenmergeprops (mergeProps) { return typeof mergeProps === 'function' ? wrapMergePropsFunc(mergeProps) : Export function whenMergePropsIsOmitted(mergeProps) {return! mergeProps ? () => defaultMergeProps : undefined } export default [whenMergePropsIsFunction, whenMergePropsIsOmitted]Copy the code

It’s basically a straightforward amalgamation of stateProps, dispatchProps, and ownProps with some basic checks.

We now have three main functions: initMapStateToProps, initMapDispatchToProps, and initMergeProps. We saw how react-Redux calculates the props of the connected component using the parameters we passed in with store.

Let’s take a closer look at how the selectorFactory function is based on our init… Functions to calculate the final props.

selectorFactory

export default function finalPropsSelectorFactory( dispatch, { initMapStateToProps, initMapDispatchToProps, initMergeProps, ... Options}) {// You can see that init... Const mapStateToProps = initMapStateToProps(dispatch, options) const mapDispatchToProps = initMapDispatchToProps(dispatch, options) const mergeProps = initMergeProps(dispatch, options) if (process.env.NODE_ENV ! == 'production') { verifySubselectors( mapStateToProps, mapDispatchToProps, mergeProps, Options. displayName)} // Select different functions based on options.pure for next calculation // React Redux We can pass this configuration in createStore, which defaults to true. // When Pure is true, React Redux has several internal optimizations, such as the selectFactory we see here. When pure is different, different functions are selected for the calculation of props. If our pure is false, then each time it evaluates to generate new props, which is passed to our internal component, rerender // when our pure is true, React Redux will cache the result of the previous calculation. If they are the same, it will return the result of the previous calculation. Once our component is pure, passing the same props does not cause the component rerender, Const selectorFactory = options.pure? pureFinalPropsSelectorFactory : impureFinalPropsSelectorFactory return selectorFactory( mapStateToProps, mapDispatchToProps, mergeProps, dispatch, options ) }Copy the code

impureFinalPropsSelectorFactory

When the pure is false, call impureFinalPropsSelectorFactory props

// This returns new props for each evaluation, Lead to the component has been rerender export function impureFinalPropsSelectorFactory (mapStateToProps mapDispatchToProps, mergeProps, dispatch ) { return function impureFinalPropsSelector(state, ownProps) { return mergeProps( mapStateToProps(state, ownProps), mapDispatchToProps(dispatch, ownProps), ownProps ) } }Copy the code

pureFinalPropsSelectorFactory

When pure true, call pureFinalPropsSelectorFactory props

// Compare the results, Performance optimization export function pureFinalPropsSelectorFactory (mapStateToProps mapDispatchToProps, mergeProps, dispatch, { areStatesEqual, areOwnPropsEqual, areStatePropsEqual } ) { let hasRunAtLeastOnce = false let state let ownProps let stateProps let dispatchProps let mergedProps function handleFirstCall(firstState, State = firstState ownProps = firstOwnProps stateProps = mapStateToProps(state, OwnProps) dispatchProps = mapDispatchToProps (dispatch, ownProps) / / agree the process of calculation for the first time with impureFinalPropsSelectorFactory, MergedProps = mergeProps(stateProps, dispatchProps, ownProps) hasRunAtLeastOnce = true return mergedProps } function handleNewPropsAndNewState() { stateProps = mapStateToProps(state, ownProps) if (mapDispatchToProps.dependsOnOwnProps) dispatchProps = mapDispatchToProps(dispatch, ownProps) mergedProps = mergeProps(stateProps, dispatchProps, ownProps) return mergedProps } function handleNewProps() { if (mapStateToProps.dependsOnOwnProps) stateProps = mapStateToProps(state, ownProps) if (mapDispatchToProps.dependsOnOwnProps) dispatchProps = mapDispatchToProps(dispatch, ownProps) mergedProps = mergeProps(stateProps, dispatchProps, ownProps) return mergedProps } function handleNewState() { const nextStateProps = mapStateToProps(state, ownProps) const statePropsChanged = ! areStatePropsEqual(nextStateProps, stateProps) stateProps = nextStateProps if (statePropsChanged) mergedProps = mergeProps(stateProps, dispatchProps, OwnProps) return mergedProps} // During the subsequent props calculation, different functions are selected based on the changes of the state and props to minimize the amount of computation and optimize the performance. // If neither the state nor the props are changed, Function handleSubsequentCalls(nextState, nextOwnProps) {const propsChanged =! areOwnPropsEqual(nextOwnProps, ownProps) const stateChanged = ! AreStatesEqual (nextState, state) state = nextState ownProps = nextOwnProps // If both props and state are changed, HandleNewPropsAndNewState function called the if (propsChanged && stateChanged) return handleNewPropsAndNewState () / / if props change, Call handleNewProps if (propsChanged) return handleNewProps() if stateChanged changes Call handleNewState if (stateChanged) return handleNewState() // If the props and state are not changed, MergedProps return function pureFinalPropsSelector(nextState, nextOwnProps) { return hasRunAtLeastOnce ? handleSubsequentCalls(nextState, nextOwnProps) : handleFirstCall(nextState, nextOwnProps) } }Copy the code

You can see that there are several functions in this code to compare variables: areOwnPropsEqual, areStatesEqual, and areStatePropsEqual. In the previous article we also saw the areMergedPropsEqual function, which was assigned when the connect function was defined

{ pure = true, areStatesEqual = strictEqual, areOwnPropsEqual = shallowEqual, areStatePropsEqual = shallowEqual, areMergedPropsEqual = shallowEqual, ... extraOptions } = {}Copy the code

strictEqual

function strictEqual(a, b) {
  return a === b
}
Copy the code

shallowEqual

function is(x, y) { if (x === y) { return x ! == 0 || y ! == 0 || 1 / x === 1 / y } else { return x ! == x && y ! == y } } export default function shallowEqual(objA, objB) { if (is(objA, objB)) return true if ( typeof objA ! == 'object' || objA === null || typeof objB ! == 'object' || objB === null ) { return false } const keysA = Object.keys(objA) const keysB = Object.keys(objB) if (keysA.length ! == keysB.length) return false for (let i = 0; i < keysA.length; i++) { if ( ! Object.prototype.hasOwnProperty.call(objB, keysA[i]) || ! is(objA[keysA[i]], objB[keysA[i]]) ) { return false } } return true }Copy the code

You can see the contrast of the former is simple and rough, and the contrast of the latter is more delicate.

Why is the comparison of state different from the other three?

This is because state is special. Check out the source code of Redux: Combinereducers.ts. It is not difficult to find that when all the contents returned by our reducers remain unchanged (the original reference is maintained), the state we finally obtained (the object returned by store.getState ()) will also maintain the original reference, making oldState === newState established. So we’ll compare state in React Redux much easier than the other three.

Why can Redux ensure that the reducer returns the original state when the reducer does not modify the state, but the reference relationship of the reducer modified state must have changed? This is because REdux requires users to follow these requirements when defining reducer. When reducer produces new data, new objects must be created (by extending the syntax… Or Object.assisgn), which must return the old Object if no action. Type is matched. This can be seen in the previously mentioned combineReducers source code can still see many targeted checks.

We can actually see that selectorFactory does conform to the standard format mentioned earlier:

(dispatch, options) => (nextState, nextOwnProps) => nextFinalProps

Now that you know how selectorFactory works, let’s look at how it is used inside connectAdvanced.

connectAdvanced

Open the SRC/components/connectAdvanced js file, you can see the function after a few to do their part in front of the check, direct return a function has a large section of the code: WrapWithConnect, this function is about 300 lines long, and it’s what we call connect(…). And then the function that returns, as you can imagine, takes a UI component that we defined, and returns us a container component.

Let’s go through the code and pick out the most important ones.

const selectorFactoryOptions = { ... connectOptions, getDisplayName, methodName, renderCountProp, shouldHandleStateChanges, storeKey, displayName, wrappedComponentName, WrappedComponent } const { pure } = connectOptions function createChildSelector(store) { return selectorFactory(store.dispatch, selectorFactoryOptions) }Copy the code

This code builds a selectorFactoryOptions object based on the parameters, and then creates a new createChildSelector function that calls the selectorFactory function we analyzed earlier. We know that the selectorFactory function conforms to our standard format, so calling selectorFactory gives us a new function that takes the props and the state and returns the props that we computed for the next rendering. So the result of createChildSelector is a props function. The reason for doing this is to calculate the props function for the current store so that it does not need to be called again when it needs to be evaluated later. This function is called again when our store object changes

Const childPropsSelector = const childPropsSelector = const childPropsSelector = const childPropsSelector = const childPropsSelector = const childPropsSelector = const childPropsSelector = const childPropsSelector = useMemo(() => { // The child props selector needs the store reference as an input. // Re-create this selector whenever the store changes. return createChildSelector(store) }, [store])Copy the code

After doing some pre-work, we internally define a ConnectFunction, which is the component we’ll actually use for rendering and which will be returned by connect()(). When we pass props to a container component, we pass ConnectFunction

ConnectFunction

const [propsContext, forwardedRef, wrapperProps] = useMemo(() => { const { forwardedRef, ... wrapperProps } = props return [props.context, forwardedRef, wrapperProps] }, [props]) // Calculate the Context to be used based on propsContext and Context, where propsContext is passed externally, // If ContainerComponent receives the Context property, it uses the Context it passed in, otherwise it uses the one defined by context.js. // That's also the context that provides the store object. const ContextToUse = useMemo(() => { return propsContext && propsContext.Consumer && isContextConsumer(<propsContext.Consumer />) ? PropsContext: Context}, [propsContext, Context]) // Use the Context via useContext and subscribe to its changes, re-render when the Context changes. Const contextValue = useContext(ContextToUse) // Check if there is a store in the props and context. // So we can also pass ContainerComponent a store props as our connect store const didStoreComeFromProps = Boolean(props.store) && Boolean(props.store.getState) && Boolean(props.store.dispatch) const didStoreComeFromContext = Boolean(contextValue) && Boolean(contextValue.store) // Uses the store passed in. If ContainerComponent receives a store, it uses it as a store. // It is possible to pass the props of the DisplayComponent into the store, and if it does, the component will use the store of the props instead of the context. const store = didStoreComeFromProps ? props.store : contextValue.storeCopy the code

The props in this code is the props we pass to the container component, from which we parse out our forwardedRef, context, and other props properties. The forwardedRef is used to transfer the ref property we set on the container component to the internal UI component via the React.

The context property is used to calculate the context that we’re going to use. Other props Calculates the props used by the UI component.

When deciding which context to use, use the values passed by it through the useContext API, so we use React Hooks. We use the useContext API to access the contents of the Provider. Without using the Consumer component.

React Redux will use the store as the data source if we pass the store property to our container component. It’s not a store object in the top-level Context.

In the second half of the function, we have the following code:

const usePureOnlyMemo = pure ? UseMemo: callback => callback() // Finally use the props const actualChildProps = usePureOnlyMemo(() => {//... Ignore the part of the code return childPropsSelector (store. GetState (), wrapperProps)}, [store, previousStateUpdateResult wrapperProps])Copy the code

As you can see, this is also based on the options.pure option, If not true will update each store, previousStateUpdateResult or wrapperProps leads to actualChildProps recount.

So the actualChildProps here is the nextFinalProps in the specification above us.

After calculating the final props, we can render our UI component:

const renderedWrappedComponent = useMemo( () => <WrappedComponent {... actualChildProps} ref={forwardedRef} />, [forwardedRef, WrappedComponent, actualChildProps] )Copy the code

The WrappedComponent in the code above is the UI component we passed in, and you can see that the forwardedRef ends up pointing to the internal UI component.

The renderedWrappedComponent is the result of our rendering of the UI component. However, we cannot directly render it back to the user. We also need to consider other cases, such as how the UI component subscrires to store updates.

Write in the last

Ape road difficult to walk, and line and cherish ~~~~~~