目录
React-redux框架基本原理
是什么?
React-redux是一个将react和redux组合在一起,帮助简化开发的框架
为什么?
React中的是view层的框架,Redux是对前端状态管理的又一层抽象。React-redux是将二者连接起来的框架。
下面将的前提是,你对React和Redux的基本概念都比较了解。
- 1)view层需要根据model的数据,改变view,react需要传入props
- 2)model层需要根据view层触发的action,改变store
所以最基本的需求是: - a) React组件需要subscribe store中的数据改变,实时渲染界面
- b) React组件需要把用户的操作转变为dispatch(action)
当然,如果没有React-redux,自己每次都写订阅函数或者触发action,那也是可以的。作为编程人员,当然需要一个接入口,让工作更集中在逻辑处理中,所以React-redux就形成了。
怎么用?
React-redux的使用非常简单,
- 在React组件最外层包裹一层,Provider组件;
- 在子组件中,通过connect将组件和store组合起来。
// 一般的操作是这样的
<Provider store={store}>
{router}
</Provider>
// 对于子组件
@connect((state) => ({}), {actions})
export default class SonReactComponent extends React.Component {
render() {
return ( <div>
// your code here
... ...
</div>);
}
}
框架源码模块划分
从源码可以看出,用户接触到的React-redux,只有两部分,Provider和connect,
接下来,我们就从这两个部分入手,看看如何实现两个a和b两个基本需求的。
Provider
Provider就是React的一个组件,利用React的context概念,将数据传递给各个组件
⚠️注意:这里的context是React 15.x版本的写法
主要是两个主要概念:
getChildContext:当state或者props改变的时候,就会调用这个,所有的子组件都会收到。
childContextTypes:定义给子组件的context类型
function createProvider(storeKey = 'store') {
const subscriptionKey = `${storeKey}Subscription`
class Provider extends Component {
getChildContext() {
return { [storeKey]: this[storeKey], [subscriptionKey]: null }
}
constructor(props, context) {
super(props, context)
this[storeKey] = props.store;
}
render() {
return Children.only(this.props.children)
}
}
Provider.propTypes = {
store: storeShape.isRequired,
children: PropTypes.element.isRequired,
}
Provider.childContextTypes = {
[storeKey]: storeShape.isRequired,
[subscriptionKey]: subscriptionShape,
}
return Provider
}
那既然所有的子组件都会收到更新,那么对于子组件,有一问题
并不是所有的store改变,我都需要改变,所以需要订阅特定的,如果不必要的更新,就不做操作,接下来我们看看connect函数的实现。
Connect
connect是对connectAdvanced进行了包装之后的函数,connect函数主要做了这样一个工作:将state和dispatch合并为一个参数
(dispatch, options) => (nextState, nextOwnProps) => nextFinalProps
下面的代码,就是connect通过connectHOC将各个状态返回为一个对象
export function createConnect({
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, {
methodName: 'connect',
getDisplayName: name => `Connect(${name})`,
shouldHandleStateChanges: Boolean(mapStateToProps),
initMapStateToProps,
initMapDispatchToProps,
initMergeProps,
pure,
areStatesEqual,
areOwnPropsEqual,
areStatePropsEqual,
areMergedPropsEqual,
...extraOptions
})
}
}
export default createConnect()
接下来我们就对这几个函数进行说明:connectAdvanced、defaultMapStateToPropsFactories、defaultMapDispatchToPropsFactories、defaultMergePropsFactories、defaultSelectorFactory
defaultSelectorFactory
defaultSelectorFactory是传给connectAdvanced的第一个参数,是一个高阶函数,
对传入的函数进行组合变化。
有两个基本的函数:
- pureFinalPropsSelectorFactory:props没有变化
- impureFinalPropsSelectorFactory:props有变化
上面这两个其实做了一件事情,** merge props和merge state**
export default function finalPropsSelectorFactory(dispatch, {
initMapStateToProps,
initMapDispatchToProps,
initMergeProps,
...options
}) {
const mapStateToProps = initMapStateToProps(dispatch, options)
const mapDispatchToProps = initMapDispatchToProps(dispatch, options)
const mergeProps = initMergeProps(dispatch, options)
// 如果props和state没有改变,options.prue == true ,状态不会改变,如果为false,总是返回一个新的对象
const selectorFactory = options.pure
? pureFinalPropsSelectorFactory
: impureFinalPropsSelectorFactory
return selectorFactory(
mapStateToProps,
mapDispatchToProps,
mergeProps,
dispatch,
options
)
}
pureFinalPropsSelectorFactory
进行了两个方面的操作:
- 如果是第一次初始化,就把状态merge一下
- 如果不是第一次, 根据props改变还是state变化,再把merge结果返回
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, firstOwnProps) {
state = firstState
ownProps = firstOwnProps
stateProps = mapStateToProps(state, ownProps)
dispatchProps = mapDispatchToProps(dispatch, ownProps)
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
}
function handleSubsequentCalls(nextState, nextOwnProps) {
const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps)
const stateChanged = !areStatesEqual(nextState, state)
state = nextState
ownProps = nextOwnProps
if (propsChanged && stateChanged) return handleNewPropsAndNewState()
if (propsChanged) return handleNewProps()
if (stateChanged) return handleNewState()
return mergedProps
}
return function pureFinalPropsSelector(nextState, nextOwnProps) {
return hasRunAtLeastOnce
? handleSubsequentCalls(nextState, nextOwnProps)
: handleFirstCall(nextState, nextOwnProps)
}
}
impureFinalPropsSelectorFactory
做了一件事情,直接merge完事了。
function impureFinalPropsSelectorFactory(
mapStateToProps,
mapDispatchToProps,
mergeProps,
dispatch
) {
return function impureFinalPropsSelector(state, ownProps) {
return mergeProps(
mapStateToProps(state, ownProps),
mapDispatchToProps(dispatch, ownProps),
ownProps
)
}
}
defaultMapStateToPropsFactories和defaultMapDispatchToPropsFactories
这两个也是一个包装函数,真实的函数是wrapMapToPropsFunc
function wrapMapToPropsFunc(mapToProps, methodName) {
return function initProxySelector(dispatch, { displayName }) {
const proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
return proxy.dependsOnOwnProps
? proxy.mapToProps(stateOrDispatch, ownProps)
: proxy.mapToProps(stateOrDispatch)
}
proxy.dependsOnOwnProps = true
proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch, ownProps) {
proxy.mapToProps = mapToProps
proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
let props = proxy(stateOrDispatch, ownProps)
if (typeof props === 'function') {
proxy.mapToProps = props
proxy.dependsOnOwnProps = getDependsOnOwnProps(props)
props = proxy(stateOrDispatch, ownProps)
}
return props
}
return proxy
}
}
// 如果是true,就是不依赖父组件的参数,如果是0,意味着具体的参数个数不确定
export function getDependsOnOwnProps(mapToProps) {
return (mapToProps.dependsOnOwnProps !== null && mapToProps.dependsOnOwnProps !== undefined)
? Boolean(mapToProps.dependsOnOwnProps)
: mapToProps.length !== 1
}
defaultMergePropsFactories
function wrapMergePropsFunc(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
return mergedProps
}
}
}
connectAdvanced
connect 需要传入几个参数
connect(mapStateToProps, mapDispatchToProps, mergeProps)(Your component);
connect函数的前几个参数,通过上面的描述,是用来merge的,把所有的转变为props,接下来就是把这些props,传递给包装的组件。在最初的时候,定义了Provider,将context传递过来,后面需要一个组件获取context,包装组件
connectAdvanced函数做了一件事情:
定义connect组件,在其中定义了与Provider中context的操作。将传递的组件,包装一层,传递给connect组件。当store变化时,更新组件就可以了
import hoistStatics from 'hoist-non-react-statics'
import invariant from 'invariant'
import { Component, createElement } from 'react'
import Subscription from '../utils/Subscription'
import { storeShape, subscriptionShape } from '../utils/PropTypes'
let hotReloadingVersion = 0
const dummyState = {}
function noop() {}
function makeSelectorStateful(sourceSelector, store) {
// 把selector包装在一个对象中,用来追踪结果
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
}
export default function connectAdvanced(
// 组合的函数
selectorFactory,
{
getDisplayName = name => `ConnectAdvanced(${name})`,
methodName = 'connectAdvanced',
renderCountProp = undefined,
// 是否订阅store的变化
shouldHandleStateChanges = true,
storeKey = 'store',
withRef = false,
...connectOptions
} = {}
) {
const subscriptionKey = storeKey + 'Subscription'
const version = hotReloadingVersion++
const contextTypes = {
[storeKey]: storeShape,
[subscriptionKey]: subscriptionShape,
}
const childContextTypes = {
[subscriptionKey]: subscriptionShape,
}
return function wrapWithConnect(WrappedComponent) {
// 定义一个名字
const wrappedComponentName = WrappedComponent.displayName
|| WrappedComponent.name
|| 'Component'
const displayName = getDisplayName(wrappedComponentName)
const selectorFactoryOptions = {
...connectOptions,
getDisplayName,
methodName,
renderCountProp,
shouldHandleStateChanges,
storeKey,
withRef,
displayName,
wrappedComponentName,
WrappedComponent
}
class Connect extends Component {
constructor(props, context) {
super(props, context)
this.version = version
this.state = {}
this.renderCount = 0
this.store = props[storeKey] || context[storeKey]
this.propsMode = Boolean(props[storeKey])
this.setWrappedInstance = this.setWrappedInstance.bind(this)
this.initSelector()
this.initSubscription()
}
getChildContext() {
const subscription = this.propsMode ? null : this.subscription
return { [subscriptionKey]: subscription || this.context[subscriptionKey] }
}
componentDidMount() {
if (!shouldHandleStateChanges) return
this.subscription.trySubscribe()
this.selector.run(this.props)
if (this.selector.shouldComponentUpdate) this.forceUpdate()
}
componentWillReceiveProps(nextProps) {
this.selector.run(nextProps)
}
shouldComponentUpdate() {
return this.selector.shouldComponentUpdate
}
componentWillUnmount() {
if (this.subscription) this.subscription.tryUnsubscribe()
this.subscription = null
this.notifyNestedSubs = noop
this.store = null
this.selector.run = noop
this.selector.shouldComponentUpdate = false
}
getWrappedInstance() {
return this.wrappedInstance
}
setWrappedInstance(ref) {
this.wrappedInstance = ref
}
initSelector() {
const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)
this.selector = makeSelectorStateful(sourceSelector, this.store)
this.selector.run(this.props)
}
initSubscription() {
if (!shouldHandleStateChanges) return
// 订阅 store的变化
const parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey]
this.subscription = new Subscription(this.store, parentSub, this.onStateChange.bind(this))
this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription)
}
onStateChange() {
this.selector.run(this.props)
if (!this.selector.shouldComponentUpdate) {
this.notifyNestedSubs()
} else {
this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate
this.setState(dummyState)
}
}
notifyNestedSubsOnComponentDidUpdate() {
this.componentDidUpdate = undefined
this.notifyNestedSubs()
}
isSubscribed() {
return Boolean(this.subscription) && this.subscription.isSubscribed()
}
addExtraProps(props) {
if (!withRef && !renderCountProp && !(this.propsMode && this.subscription)) return props
const withExtras = { ...props }
if (withRef) withExtras.ref = this.setWrappedInstance
if (renderCountProp) withExtras[renderCountProp] = this.renderCount++
if (this.propsMode && this.subscription) withExtras[subscriptionKey] = this.subscription
return withExtras
}
render() {
const selector = this.selector
selector.shouldComponentUpdate = false
if (selector.error) {
throw selector.error
} else {
return createElement(WrappedComponent, this.addExtraProps(selector.props))
}
}
}
Connect.WrappedComponent = WrappedComponent
Connect.displayName = displayName
Connect.childContextTypes = childContextTypes
Connect.contextTypes = contextTypes
Connect.propTypes = contextTypes
// 把WrappedComponent的非React属性拷贝到Connect上。下面详细说下Connect。
return hoistStatics(Connect, WrappedComponent)
}
}
当参数发生变化的时候,可以更新组件,那么我们什么时候可以知道store更新了呢?可以看到
Subscription
const CLEARED = null
const nullListeners = { notify() {} }
function createListenerCollection() {
let current = []
let next = []
return {
clear() {
next = CLEARED
current = CLEARED
},
notify() {
const listeners = current = next
for (let i = 0; i < listeners.length; i++) {
listeners[i]()
}
},
get() {
return next
},
subscribe(listener) {
let isSubscribed = true
if (next === current) next = current.slice()
next.push(listener)
return function unsubscribe() {
if (!isSubscribed || current === CLEARED) return
isSubscribed = false
if (next === current) next = current.slice()
next.splice(next.indexOf(listener), 1)
}
}
}
}
export default class Subscription {
constructor(store, parentSub, onStateChange) {
this.store = store
this.parentSub = parentSub
this.onStateChange = onStateChange
this.unsubscribe = null
this.listeners = nullListeners
}
addNestedSub(listener) {
this.trySubscribe()
return this.listeners.subscribe(listener)
}
notifyNestedSubs() {
this.listeners.notify()
}
isSubscribed() {
return Boolean(this.unsubscribe)
}
trySubscribe() {
if (!this.unsubscribe) {
// !!!!!!!!!!!!!看到没,看到没,在这里订阅了store的变化
this.unsubscribe = this.parentSub
? this.parentSub.addNestedSub(this.onStateChange)
: this.store.subscribe(this.onStateChange)
this.listeners = createListenerCollection()
}
}
tryUnsubscribe() {
if (this.unsubscribe) {
this.unsubscribe()
this.unsubscribe = null
this.listeners.clear()
this.listeners = nullListeners
}
}
}
总结(前面没看不要紧,这里一定要看!!!)
总结起来,react-redux源码完成了基本的需求:订阅更新和触发action
其中用到最主要的是
- 1、利用进行订阅,store.subscribe
- 2、将store的state和dispatch作为props传递给React组件