React-redux Provider react-redux Provider react-redux Provider react-redux Provider react-redux Provider
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — line — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — Above, we analyzed the mapStateToPropsFactories, mapDispatchToPropsFactories and mergePropsFactor, here we want to formally introduce the core of the connect method, connectAdvanced. This method is more code, let’s introduce step by step.
Export default function connectAdvanced(selectorFactory, // options object: { getDisplayName = (name) => `ConnectAdvanced(${name})`, methodName = 'connectAdvanced', renderCountProp = undefined, shouldHandleStateChanges = true, storeKey = 'store', withRef = false, forwardRef = false, context = ReactReduxContext, // additional options are passed through to the selectorFactory ... connectOptions } = {} ) { return function wrapWithConnect(WrappedComponent) { .... const Connect = pure ? React.memo(ConnectFunction) : ConnectFunction Connect.WrappedComponent = WrappedComponent Connect.displayName = displayName if (forwardRef) { const forwarded = React.forwardRef(function forwardConnectRef( props, ref ) { return <Connect {... props} reactReduxForwardedRef={ref} /> }) forwarded.displayName = displayName forwarded.WrappedComponent = WrappedComponent return hoistStatics(forwarded, WrappedComponent) } return hoistStatics(Connect, WrappedComponent) }) })Copy the code
The function takes two arguments :selectorFactory and connectOptions(optional), returns a higher-order component wrapWithConnect(implemented as a property broker), Connect was created in the high-order component, and hoistStatics was returned (to solve the high-order component static method loss problem, automatically copy all non-React static methods). ForwardRef = forwardRef (forwardRef) The overall code is fairly simple, nothing hard to understand. So it looks like the key is in ConnectFunction. I didn’t look at the code:
1. // 2. function createChildSelector(store) { 3. return selectorFactory(store.dispatch, SelectorFactoryOptions) 4.} 6. // In pure mode, using usememo, this Component automatically makes a shallow comparison of props when changes. Update components if they are not equal. 7. const usePureOnlyMemo = pure ? useMemo : (callback) => callback() 8. 9. // 10. function ConnectFunction(props) { 11. const [ 12. propsContext, 13. reactReduxForwardedRef, 14. wrapperProps, 15. ] = useMemo(() => { 16. 17. const { reactReduxForwardedRef, ... wrapperProps } = props 18. return [props.context, reactReduxForwardedRef, wrapperProps] 19. }, [props]) 20. 21. const ContextToUse = useMemo(() => { 22. return propsContext && 23. propsContext.Consumer && 24. isContextConsumer(<propsContext.Consumer />) 25. ? propsContext 26. : Context 27. }, [propsContext, Context]) 28. 29. const contextValue = useContext(ContextToUse) 30. 31. const didStoreComeFromProps = 32. Boolean(props.store) && 33. Boolean(props.store.getState) && 34. Boolean(props.store.dispatch) 35. const didStoreComeFromContext = 36. Boolean(contextValue) && Boolean(contextValue.store) 37. 38. if ( 39. process.env.NODE_ENV ! == 'production' && 40. ! didStoreComeFromProps && 41. ! DidStoreComeFromContext 42.) {43. ··· 44.} 45. props.store : contextValue.store 47. 48. const childPropsSelector = useMemo(() => { 49. 50. return createChildSelector(store) 51. }, [store]) 52. 53. const [subscription, notifyNestedSubs] = useMemo(() => { 54. if (! shouldHandleStateChanges) return NO_SUBSCRIPTION_ARRAY 55. 56. const subscription = new Subscription( 57. store, 58. didStoreComeFromProps ? null : contextValue.subscription 59. ) 60. 61. const notifyNestedSubs = subscription.notifyNestedSubs.bind( 62. subscription 63. ) 64. 65. return [subscription, notifyNestedSubs] 66. }, [store, didStoreComeFromProps, contextValue]) 67. 68. 69. const overriddenContextValue = useMemo(() => { 70. if (didStoreComeFromProps) { 71. 72. return contextValue 73. } 74. 75. 76. return { 77. ... contextValue, 78. subscription, 79. } 80. }, [didStoreComeFromProps, contextValue, subscription]) 81. 82. 83. const [ 84. [previousStateUpdateResult], 85. forceComponentUpdateDispatch, 86. ] = useReducer(storeStateUpdatesReducer, EMPTY_ARRAY, initStateUpdates) 87. 88. if (previousStateUpdateResult && previousStateUpdateResult.error) { 89. throw previousStateUpdateResult.error 90. } 91. 92. 93. const lastChildProps = useRef() // The last childProps (props passed to WrappedComponent, Record here is the last props) 94. The const lastWrapperProps = useRef (wrapperProps) / / props of the parent elements passed in 95. The const childPropsFromStoreUpdate Const renderIsScheduled = useRef() const renderIsScheduled = useRef() actualChildProps = usePureOnlyMemo(() => { 99. 100. if ( 101. childPropsFromStoreUpdate.current && 102. wrapperProps === lastWrapperProps.current 103. ) { 104. return childPropsFromStoreUpdate.current 105. } 106. 107. return childPropsSelector(store.getState(), wrapperProps) 108. }, [store, previousStateUpdateResult, wrapperProps]) 109. 110. useIsomorphicLayoutEffectWithArgs(captureWrapperProps, [ 111. lastWrapperProps, 112. lastChildProps, 113. renderIsScheduled, 114. wrapperProps, 115. actualChildProps, 116. childPropsFromStoreUpdate, 117. notifyNestedSubs, 118. ]) 119. 120. useIsomorphicLayoutEffectWithArgs( 121. subscribeUpdates, 122. [ 123. shouldHandleStateChanges, 124. store, 125. subscription, 126. childPropsSelector, 127. lastWrapperProps, 128. lastChildProps, 129. renderIsScheduled, 130. childPropsFromStoreUpdate, 131. notifyNestedSubs, 132. forceComponentUpdateDispatch, 133. ], 134. [store, subscription, childPropsSelector] 135. ) 136. 137. const renderedWrappedComponent = useMemo( 138. () => ( 139. <WrappedComponent 140. {... actualChildProps} 141. ref={reactReduxForwardedRef} 142. /> 143. ), 144. [reactReduxForwardedRef, WrappedComponent, actualChildProps] 145. ) 146. 147. const renderedChild = useMemo(() => { 148. if (shouldHandleStateChanges) { 149. 150. return ( 151. <ContextToUse.Provider value={overriddenContextValue}> 152. {renderedWrappedComponent} 153. </ContextToUse.Provider> 154. ) 155. } 156. 157. return renderedWrappedComponent 158. }, [ContextToUse, renderedWrappedComponent, overriddenContextValue]) 159. 160. return renderedChild 161. }Copy the code
So let’s look at line 46 and fetch the store from contextValue. We then get the childPropsSelector, which in line 98 calculates the new actualChildProps for rendering child components with Store.getState () and wrapperProps.
Further down you get subscription and notifyNestedSubs, which were mentioned in the previous article. Here the Subscripion is an event object, and notifyNestedSubs notifies all listeners of subscription that an event has occurred and that a callback is executed.
See line 83. Here is the key to cause component ConnectFunction update, only call forceComponentUpdateDispatch function, component ConnectFunction will update.
The storeStateUpdatesReducer directly returns an array. The first item is the payload of the dispatch action. Content is forceComponentUpdateDispatch into arguments and previousStateUpdateResult attribute values.
Line 98 starts calculating the actualChildProps, actualChildProps. If the store and props from the parent element have not been updated, return the childProps calculated by the store update. Otherwise, re-evaluate the childProps via childPropsSelector.
The captureWrapperProps function is used to cache the values of wrapperProps and actualChildProps. If the update is due to a store change, subscription subscribers are notified that they need to pull the latest state.
Then you see subscribeUpdates. Mainly subscription renewal checkForUpdates, link up the subscription instance and the forceComponentUpdateDispatch checkForUpdates.
Let’s look at checkForUpdates again:
1. const checkForUpdates = () => { 2. if (didUnsubscribe) { 3. return 4. } 5. 6. const latestStoreState = store.getState() 7. 8. let newChildProps, error 9. try { 10. 11. newChildProps = childPropsSelector( 12. latestStoreState, 13. lastWrapperProps.current 14. ) 15. } catch (e) { 16. error = e 17. lastThrownError = e 18. } 19. 20. if (! error) { 21. lastThrownError = null 22. } 23. 24. if (newChildProps === lastChildProps.current) { 25. if (! renderIsScheduled.current) { 26. notifyNestedSubs() 27. } 28. } else { 29. 30. lastChildProps.current = newChildProps 31. childPropsFromStoreUpdate.current = newChildProps 32. renderIsScheduled.current = true 33. 34. forceComponentUpdateDispatch({ 35. type: 'STORE_UPDATED', 36. payload: { 37. error, 38. }, 39. }) 40. } 41. }Copy the code
If the last childProps is the same, then the current component does not need to update. A component that does not need to be updated needs to be separately notified to the subscription listener that state is updated, because each component listens for a different state, and while the current component is not updated, other components get a new state to update.
Look at line 30. Update the current forceComponentUpdateDispatch component needs to call methods, And set the cache lastChildProps. Current = newChildProps and childPropsFromStoreUpdate. Current = newChildProps, The childPropsFromStoreUpdate. Current will be informed when forceComponentUpdateDispatch update captureWrapperProps function, through notifyNestedSubs, Notify Subscription that there is a state update.
At this point, the general introduction of CONNECT has been completed. If you are interested, you can refer to the article and comb the source code again. Maybe there will be interesting discoveries.