内容简介:上一次我们讲解了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!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Everything Store
Brad Stone / Little, Brown and Company / 2013-10-22 / USD 28.00
The definitive story of Amazon.com, one of the most successful companies in the world, and of its driven, brilliant founder, Jeff Bezos. Amazon.com started off delivering books through the mail. Bu......一起来看看 《The Everything Store》 这本书的介绍吧!