react-redux提供connect和Provider将react和redux连接起来。
- connect:用于创建容器组件,可以使容器组件访问到Provider组件通过context提供的store,并将mapStateToProps和mapDispatchToProps返回的state和dispatch传递给UI组件。
- Provider:通过context向子组件提供store
1、connect和Provider的使用
// 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'))
容器组件
// 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)
2、源码解析
先看一看react-redux包的目录结构,其中es目录适用于ES模块导入,lib适用于commonjs模块导入
2.1、Provider源码解析
Provider组件在Provider.js里面定义,仅有短短几十行代码,核心代码如下:
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钩子函数,只有在第二个参数发生变化时,第一个参数函数才会执行,可以提升代码执行性能,避免每次组件渲染都要执行函数。详情可以去查看官网,这里制作简单介绍。
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>组件包裹
2.2、connect源码解析
connect使用方式如下:
connect(mapStateToProps, mapDispatchToProps)(Demo)
可以猜想到connect(mapStateToProps, mapDispatchToProps)
这部分将返回一个高阶组件,这个高阶组件的作用就是将mapStateToProps返回的state和mapDispatchToProps返回的dispatch通过props传递给Demo。我们通过源码来验证一下猜想是否正确。
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...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。