react-redux提供connect和Provider將react和redux鏈接起來。react
// App.jsx import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' import createStore from 'redux' import reducer from './reducers' import Container from './Container' const store = createStore(reducer) const App = () => { return ( <Provider store={store}> <Container /> </Provider> ) } render(<App />, document.getElementById('app'))
容器組件git
// Container.jsx import React from 'react' import { connect } from 'react-redux' const mapStateToProps = (state, ownProps) => ({}) const mapDispatchToProps = (dispatch, ownProps) => ({}) export default connect(mapStateToProps, mapDispatchToProps)(Demo)
先看一看react-redux包的目錄結構,其中es目錄適用於ES模塊導入,lib適用於commonjs模塊導入
github
Provider組件在Provider.js裏面定義,僅有短短几十行代碼,核心代碼以下:redux
import { ReactReduxContext } from './Context'; function Provider(_ref) { var store = _ref.store, // 獲取組件綁定的store context = _ref.context, children = _ref.children; // 獲取子組件 // contextValue的值爲{store, subscription} var contextValue = useMemo(function () { var subscription = new Subscription(store); subscription.onStateChange = subscription.notifyNestedSubs; return { store: store, subscription: subscription }; }, [store]); var previousState = useMemo(function () { return store.getState(); }, [store]); useEffect(function () { var subscription = contextValue.subscription; subscription.trySubscribe(); if (previousState !== store.getState()) { subscription.notifyNestedSubs(); } return function () { subscription.tryUnsubscribe(); subscription.onStateChange = null; }; }, [contextValue, previousState]); // 若是Provider組件上綁定了context就是用綁定的context,若是沒有綁定context,就會本身生成context // children爲嵌套在Provider裏層的子組件 var Context = context || ReactReduxContext; return React.createElement(Context.Provider, { value: contextValue }, children); } export default Provider;
源碼中使用了useMemo鉤子函數,只有在第二個參數發生變化時,第一個參數函數纔會執行,能夠提高代碼執行性能,避免每次組件渲染都要執行函數。詳情能夠去查看官網,這裏製做簡單介紹。segmentfault
var Context = context || ReactReduxContext; return React.createElement(Context.Provider, { value: contextValue }, children);
咱們看看這部分代碼,若是Provider組件上綁定了context就是用綁定的context,若是沒有綁定context,就會本身生成context。ReactReduxContext的生成在Context.js中:數組
import React from 'react'; export var ReactReduxContext = /*#__PURE__*/ React.createContext(null); if (process.env.NODE_ENV !== 'production') { ReactReduxContext.displayName = 'ReactRedux'; } export default ReactReduxContext;
有了context就能夠向子組件提供store。架構
<Provider store={store}> <Container /> </Provider> // 等價於 <Provider store={store}> <Context.Provider value={{value: contextValue}}> <Container /> </Context.Provider> </Provider>
打開react devtool能夠看到最外層組件爲<Provider>,裏層的子組件由<ReactRedux.Provider>組件包裹
app
connect使用方式以下:dom
connect(mapStateToProps, mapDispatchToProps)(Demo)
能夠猜測到connect(mapStateToProps, mapDispatchToProps)
這部分將返回一個高階組件,這個高階組件的做用就是將mapStateToProps返回的state和mapDispatchToProps返回的dispatch經過props傳遞給Demo。咱們經過源碼來驗證一下猜測是否正確。ide
connect函數在connect.js中實現,函數架子大概就是以下樣子:
export function createConnect(_temp) { // coding... return function connect(mapStateToProps, mapDispatchToProps, mergeProps, _ref2) { // coding... return connectHOC(selectorFactory, options); }; } export default createConnect();
connectHOC函數執行返回的是一個高階組件wrapWithConnect(WrappedComponent),它在connectAdvanced.js中實現,connectAdvanced這個函數就是connectHOC。
export default function connectAdvanced(selectorFactory, _ref) { // coding... return function wrapWithConnect(WrappedComponent) { // coding... function createChildSelector(store) { return selectorFactory(store.dispatch, selectorFactoryOptions); } // coding... function ConnectFunction(props) { // coding... // 獲取context對象 var ContextToUse = useMemo(function () { return propsContext && propsContext.Consumer && isContextConsumer(React.createElement(propsContext.Consumer, null)) ? propsContext : Context; }, [propsContext, Context]); // 獲取Context.Provider綁定的value值{store,subscription} var contextValue = useContext(ContextToUse); // 獲取store var store = didStoreComeFromProps ? props.store : contextValue.store; // childPropsSelector返回一個函數(),接受store.getState()和props var childPropsSelector = useMemo(function () { return createChildSelector(store); }, [store]); // 這裏執行childPropsSelector,將store.getState()和props傳遞進去,而後mapStateToProps接受到state和props,至於dispatch,在執行selectorFactory(store.dispatch, selectorFactoryOptions);就傳遞進去了。 var actualChildProps = usePureOnlyMemo(function () { if (childPropsFromStoreUpdate.current && wrapperProps === lastWrapperProps.current) { return childPropsFromStoreUpdate.current; } return childPropsSelector(store.getState(), wrapperProps); }, [store, previousStateUpdateResult, wrapperProps]); // actualChildProps獲得的就是mapStateToProps返回的state,把它放在props中傳遞給UI組件 var renderedWrappedComponent = useMemo(function () { return React.createElement(WrappedComponent, _extends({}, actualChildProps, { ref: forwardedRef })); }, [forwardedRef, WrappedComponent, actualChildProps]); var renderedChild = useMemo(function () { // shouldHandleStateChanges控制是否應該訂閱redux store中的state變化。 if (shouldHandleStateChanges) { // 訂閱redux store中的state變化,返回ContextToUse.Provider嵌套組件 return React.createElement(ContextToUse.Provider, { value: overriddenContextValue }, renderedWrappedComponent); } // 不須要訂閱redux store中的state變化就直接返回UI組件 return renderedWrappedComponent; }, [ContextToUse, renderedWrappedComponent, overriddenContextValue]); return renderedChild; } // React.memo用於建立一個純函數組件,跟PureComponent同樣,但React.memo做用於function component,而PureComponent做用於class component。使用純函數組件最大的做用就是隻有props變化時組件纔會從新渲染,能夠提升渲染性能。 var Connect = pure ? React.memo(ConnectFunction) : ConnectFunction; Connect.WrappedComponent = WrappedComponent; Connect.displayName = displayName; if (forwardRef) { var forwarded = React.forwardRef(function forwardConnectRef(props, ref) { return React.createElement(Connect, _extends({}, props, { forwardedRef: ref })); }); forwarded.displayName = displayName; forwarded.WrappedComponent = WrappedComponent; return hoistStatics(forwarded, WrappedComponent); } // hoistStatics是hoist-non-react-statics包的導出,用於將組件中非react自帶的靜態方法複製到另外一個組件。該包通常用於定義HOC中,由於當你給一個組件添加一個HOC時,原來的組件會被一個container的組件包裹,這意味着新的組件不會有原來組件任何靜態方法。參考:https://zhuanlan.zhihu.com/p/36178509 return hoistStatics(Connect, WrappedComponent); }; }
connectHOC(selectorFactory, options)
中selectorFactory
函數傳遞到connectAdvanced(selectorFactory, _ref)
中,在ConnectFunction(props)
函數組件中調用createChildSelector(store)
,而後調用selectorFactory(store.dispatch, selectorFactoryOptions);
selectorFactory函數是connect中的核心API,它的實如今selectorFactory.js文件中,selectorFactory就是下面的導出。
export default function finalPropsSelectorFactory(dispatch, _ref2) { var initMapStateToProps = _ref2.initMapStateToProps, initMapDispatchToProps = _ref2.initMapDispatchToProps, initMergeProps = _ref2.initMergeProps, options = _objectWithoutPropertiesLoose(_ref2, ["initMapStateToProps", "initMapDispatchToProps", "initMergeProps"]); var mapStateToProps = initMapStateToProps(dispatch, options); var mapDispatchToProps = initMapDispatchToProps(dispatch, options); var mergeProps = initMergeProps(dispatch, options); if (process.env.NODE_ENV !== 'production') { verifySubselectors(mapStateToProps, mapDispatchToProps, mergeProps, options.displayName); } var selectorFactory = options.pure ? pureFinalPropsSelectorFactory : impureFinalPropsSelectorFactory; // return selectorFactory(mapStateToProps, mapDispatchToProps, mergeProps, dispatch, options); }
pureFinalPropsSelectorFactory函數實現:
export function pureFinalPropsSelectorFactory(mapStateToProps, mapDispatchToProps, mergeProps, dispatch, _ref) { var areStatesEqual = _ref.areStatesEqual, areOwnPropsEqual = _ref.areOwnPropsEqual, areStatePropsEqual = _ref.areStatePropsEqual; var hasRunAtLeastOnce = false; var state; var ownProps; var stateProps; var dispatchProps; var mergedProps; function handleFirstCall(firstState, firstOwnProps) { state = firstState; ownProps = firstOwnProps; stateProps = mapStateToProps(state, ownProps); dispatchProps = mapDispatchToProps(dispatch, ownProps); mergedProps = mergeProps(stateProps, dispatchProps, ownProps); hasRunAtLeastOnce = true; return mergedProps; } function handleNewPropsAndNewState() {} function handleNewProps() {} function handleNewState() {} function handleSubsequentCalls(nextState, nextOwnProps) { // coding... return function pureFinalPropsSelector(nextState, nextOwnProps) { return hasRunAtLeastOnce ? handleSubsequentCalls(nextState, nextOwnProps) : handleFirstCall(nextState, nextOwnProps); }; }
selectorFactory的做用就是將鏈接store的ConnectFunction組件中獲取的state、props傳遞給MapStateToProps和將獲取的dispatch傳遞給mapDispatchToProps。而後MapStateToProps和mapDispatchToProps的返回值會在ConnectFunction組件中使用props傳遞給UI組件。
wrapWithConnect(WrappedComponent)返回一個新的、鏈接到store的ConnectFunction(props)函數組件,該組件內部會根據shouldHandleStateChanges字段判斷是否須要監聽redux store中state的變化,若是須要就返回ContextToUse.Provider包裹UI組件的子組件,ContextToUse.Provider爲組組件提供從新構造的overriddenContextValue,若是不須要監聽redux store中state的變化,就返回UI組件爲子組件。就如第一部份內容例子,Brother組件不須要state,Sister組件須要state,那麼Sister組件就會用ContextToUse.Provider包裹着。整個組件架構就變成以下樣子:
Memo表示該組件爲純函數組件
這三篇文章很是值得一讀,參考:
https://github.com/MrErHu/blo...
https://juejin.im/post/59772a...
https://segmentfault.com/a/11...