内容简介:上一次我们讲解了Provider、connect、selectorFactory。这次主要分析 connectAdvanced 这个核心API。在开始之前我们先来看一个工具函数上篇讲过 selector 会将新的值和缓存的值做比较,如果变化,将重新求值并返回,如果没变化,返回缓存的旧值。makeSelectorStateful 函数是对 selector 的封装。正如其名字一样,使selector stateful
上一次我们讲解了Provider、connect、selectorFactory。这次主要分析 connectAdvanced 这个核心API。 react-redux源码分析及实现原型_上
connectAdvanced
在开始之前我们先来看一个 工具 函数
function makeSelectorStateful(sourceSelector, store) { 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 } 复制代码
上篇讲过 selector 会将新的值和缓存的值做比较,如果变化,将重新求值并返回,如果没变化,返回缓存的旧值。makeSelectorStateful 函数是对 selector 的封装。正如其名字一样,使selector stateful
再介绍一下 hoist-non-react-statics
这个库,作用是避免在使用HOC时,导致类的static方法丢失的问题。详情见react doc
Subscription
是实现react与redux绑定的类,在接下来会用到我们先来看一下
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) { 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 } } } 复制代码
重点在 trySubscribe 方法,如果 parentSub 存在就将回调函数绑定在父组件上,否则绑定在store.subscribe中 原因是这样可以保证组件的更新顺序,从父到子。
然后可以开始 connectAdvanced
export default function connectAdvanced( selectorFactory, { shouldHandleStateChanges = true, storeKey = 'store', // 传递给 selectorFactory 的参数 ...connectOptions } = {} ) { return function wrapWithConnect(WrappedComponent) { //... const selectorFactoryOptions = { ...connectOptions, shouldHandleStateChanges, // ... } 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)、 // selector 与 subscription 的初始化 this.initSelector() this.initSubscription() } getChildContext() { // 如果组件从props里获得store,那么将 context 中的 subscription 传递下去 // 否则就将传递此组件中的 subscription // 子组件使用祖先组件的 subscription 可以保证组件的更新顺序(父 -> 子)。 // 另外 将store通过props传递下去,这种场景是什么。。。 const subscription = this.propsMode ? null : this.subscription return { [subscriptionKey]: subscription || this.context[subscriptionKey] } } componentDidMount() { // shouldHandleStateChanges === Boolean(mapStateToProps) // 如果没有 mapStateToProps 组件不需要监听store变化 if (!shouldHandleStateChanges) return // 由于 componentWillMount 会在ssr中触发,而 componentDidMount、componentWillUnmount不会。 // 如果将subscription放在 componentWillMount中,那么 unsubscription 将不会被触发,将会导致内存泄漏。 this.subscription.trySubscribe() // 为了防止子组件在 componentWillMount 中调用dipatch 所以这里需要在重新计算一次 // 因为子组件的 componentWillMount 先于组件的 componentDidMount 发生,此时还没有执行 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 } initSelector() { const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions) this.selector = makeSelectorStateful(sourceSelector, this.store) this.selector.run(this.props) } initSubscription() { if (!shouldHandleStateChanges) return const parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey] this.subscription = new Subscription(this.store, parentSub, this.onStateChange.bind(this)) // 这里是防止组件在通知过程中卸载,此时this.subscription就为null了。这里将notifyNestedSubs拷贝一次。 // 并且在componentWillUnmount 中 this.notifyNestedSubs = noop, 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() } render() { const selector = this.selector selector.shouldComponentUpdate = false if (selector.error) { throw selector.error } else { return createElement(WrappedComponent, this.addExtraProps(selector.props)) } } } /* eslint-enable react/no-deprecated */ Connect.WrappedComponent = WrappedComponent Connect.displayName = displayName Connect.childContextTypes = childContextTypes Connect.contextTypes = contextTypes Connect.propTypes = contextTypes return hoistStatics(Connect, WrappedComponent) } } 复制代码
over~ 是不是觉得react-redux很简单? 接下来我会将react技术栈中常用的库(react, react-router, redux, redux-saga, dva)源码分析一遍,喜欢的话。可以watch me!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。