轉眼間2017年已通過了一半了,看到以前有人問是否完成了本身半年的計劃,答案是:固然沒有啦。感受本身如今對技術產生了敬畏,由於要學習的知識是在是太多了,而本身的時間和精力卻很難達到目標,目前處在比較焦慮的狀態。本身是年初進入掘金的,半年內雖然文章的閱讀量不錯可是關注度過低,半年就混了40個關注,說來真是慚愧。
扯遠了,咱們言歸正傳,上次的文章Redux:百行代碼千行文檔解釋了Redux內部的運做原理。可是咱們在React中不多會直接搭配使用Redux,而是經過React-Redux綁定React與Redux。這篇文章咱們咱們將瞭解React-Redux其中的奧祕。在閱讀以前但願你有React-Redux的使用經驗,不然這篇文章可能不太適合你。
首先咱們能夠看看React-Redux的源碼目錄結構,大體看一下,作到內心有數。javascript
.
├── components
│ ├── Provider.js
│ └── connectAdvanced.js
├── connect
│ ├── connect.js
│ ├── mapDispatchToProps.js
│ ├── mapStateToProps.js
│ ├── mergeProps.js
│ ├── selectorFactory.js
│ ├── verifySubselectors.js
│ └── wrapMapToProps.js
├── index.js
└── utilsjava
├── PropTypes.js ├── Subscription.js ├── shallowEqual.js ├── verifyPlainObject.js ├── warning.js └── wrapActionCreators.js
首先來看一下index.js:react
import Provider, { createProvider } from './components/Provider' import connectAdvanced from './components/connectAdvanced' import connect from './connect/connect' export { Provider, createProvider, connectAdvanced, connect }
咱們能夠看出來,React-Redux對外提供的API有四個:Provider
、createProvider
,connectAdvanced
,connect
。咱們將從connectAdvanced
開始介紹。git
其實我在看React-Redux源碼以前都不知道有這個API,爲了方便後面的源碼理解,咱們介紹一下connectAdvanced
:
connectAdvanced(selectorFactory, [connectOptions])
github
connectAdvanced
用來鏈接組件到Redux的store上。是connect
函數的基礎,但並無規定如何將state
、props
、dispatch
處理傳入最終的props
中。connectAdvanced
並無對產生的props作緩存來優化性能,都留給了調用者去實現。connectAdvanced
並無修改傳入的組件類,而是返回一個新的、鏈接到store的組件類。redux
參數:緩存
selectorFactory(dispatch, factoryOptions): selector(state, ownProps): props (Function),用來初始化selector
函數(在每次實例的構造函數中)。selector
函數在每次connector component
須要計算新的props(在組件傳遞新的props和store中數據發生改變時會計算新的props)都會被調用。selector
函數會返回純對象(plain object),這個對象會做爲props傳遞給被包裹的組件(WrappedComponent)安全
[connectOptions] (Object) 若是定義了該參數,用來進一步定製connector:
1. [getDisplayName] (Function): 用來計算connector component的displayName。閉包
2. [methodName] (String) 用來在錯誤信息中顯示,默認值爲connectAdvanced
3. [renderCountProp] (String): 若是定義了這個屬性,以該屬性命名的值會被以props傳遞給包裹組件。該值是組件渲染的次數,能夠追蹤沒必要要的渲染。
4. [shouldHandleStateChanges] (Boolean): 控制conntector
組件是否應該訂閱redux store中的state變化。
5. [storeKey] (String): 你想要從context和props得到store的key值,只有在須要多個store的狀況下才會用到(固然,這並非明智的選擇)。默認是store
。
6. [withRef] (Boolean): 若是是true
,存儲被包裹組件的實例,並能夠經過函數getWrappedInstance
得到該實例,默認值爲false
。
7. 在connectOptions
中額外的屬性會被傳遞給selectorFactory
函數的factoryOptions
屬性。app
返回:
函數返回一個高階組件,該高階組件將從store的state中構建的props傳遞給被包裹組件。
例如:
// 按照用戶信息選擇性傳入todos的部分信息 import * as actionCreators from './actionCreators' import { bindActionCreators } from 'redux' function selectorFactory(dispatch) { let ownProps = {} let result = {} const actions = bindActionCreators(actionCreators, dispatch) const addTodo = (text) => actions.addTodo(ownProps.userId, text) return (nextState, nextOwnProps) => { const todos = nextState.todos[nextProps.userId] const nextResult = { ...nextOwnProps, todos, addTodo } ownProps = nextOwnProps if (!shallowEqual(result, nextResult)) result = nextResult return result } } export default connectAdvanced(selectorFactory)(TodoApp)
講了這麼多,咱們看看connectAdvanced
是如何實現的,一開始原本想把全部的代碼都列出來,可是感受直接列出200多行的代碼看着確實不方便,因此咱們仍是一部分一部分介紹:
//代碼總體結構 function connectAdvanced( selectorFactory, { getDisplayName = name => `ConnectAdvanced(${name})`, methodName = 'connectAdvanced', renderCountProp = undefined, shouldHandleStateChanges = true, storeKey = 'store', withRef = false, ...connectOptions } = {} ) { return function wrapWithConnect(WrappedComponent) { class Connect extends Component { //...... return hoistStatics(Connect, WrappedComponent) } }
函數接受兩個參數:selectorFactory
與connectOptions
(可選),返回一個高階組件wrapWithConnect
(以屬性代理方式實現),高階組件中建立了組件類Connect
, 最後返回了hoistStatics(Connect, WrappedComponent)
。其中hoistStatics
來源於:
import hoistStatics from 'hoist-non-react-statics'
做用是將WrappedComponent
中的非React特定的靜態屬性(例如propTypes
就是React的特定靜態屬性)賦值到Connect
。做用有點相似於Object.assign
,可是僅複製非React特定的靜態屬性。
其實對於React-Redux之因此可使得Provider
中的任何子組件訪問到Redux中的store
並訂閱store
,無非是利用context
,使得全部子組件都能訪問store
。更進一步,咱們看看高階組件時如何實現:
let hotReloadingVersion = 0 const dummyState = {} function noop() {} function connectAdvanced( selectorFactory, { getDisplayName = name => `ConnectAdvanced(${name})`, methodName = 'connectAdvanced', renderCountProp = undefined, 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 { } return hoistStatics(Connect, WrappedComponent) } }
上面的代碼並無什麼難以理解的,connectAdvanced
中定義了subscriptionKey
、version
以及爲Connect
組件定義的contextTypes
與childContextTypes
(不瞭解context
的同窗能夠看這裏)。在高階組件中所做的就是定義組裝了selectorFactory
所用到的參數selectorFactoryOptions
。接下來介紹最重要的組件類Connect
:
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 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 if (process.env.NODE_ENV !== 'production') { Connect.prototype.componentWillUpdate = function componentWillUpdate() { // We are hot reloading! if (this.version !== version) { this.version = version this.initSelector() if (this.subscription) this.subscription.tryUnsubscribe() this.initSubscription() if (shouldHandleStateChanges) this.subscription.trySubscribe() } } }
咱們首先來看構造函數:
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() }
首先咱們先看看用來初始化selector
的initSelector
函數:
//Connect類方法 initSelector() { const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions) this.selector = makeSelectorStateful(sourceSelector, this.store) this.selector.run(this.props) } //connectAdvanced外定義的函數 function makeSelectorStateful(sourceSelector, store) { // wrap the selector in an object that tracks its results between runs. 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
的主要做用是用來從store
中的state
和ownProps
中計算新的props,並返回純對象(plain object),這個對象會做爲props傳遞給被包裹的組件(WrappedComponent)。在initSelector
中,首先調用selectorFactory
從而初始化sourceSelector
,咱們並不會直接調用sourceSelector
,而是爲了程序的健壯,經過將sourceSelector
做爲參數調用makeSelectorStateful
,返回更加安全的selector
。今後以後,咱們想要生成新的props
只須要調用selector.run
函數。在selector.run
函數中對sourceSelector
的異常作了處理,並用sourceSelector.error
記錄是否存在異常。sourceSelector.shouldComponentUpdate
用來根據先後兩次返回的props
是否相同,從而記錄是否應該刷新組件,這就爲後期的性能提高留出了空間,只要在先後數據相同時,咱們就返回同一個對象,使得shouldComponentUpdate
爲false
,就能夠避免沒必要要的刷新,固然這不是咱們selector
的職責,而是sourceSelector
所須要作的。每次返回的新的props
都會記錄在selector.props
以備後用。
再看initSubscription
函數以前,咱們須要先了解一下Subscription
類:
// 爲鏈接到redux的store的組件以及嵌套的後代組件封裝訂閱邏輯,以確保祖先組件在後代組件以前刷新 const CLEARED = null const nullListeners = { notify() {} } function createListenerCollection() { //代碼邏輯來源與store中 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]() } }, 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) { 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 } } }
首先咱們先看函數createListenerCollection
,這邊的代碼邏輯和redux
中的listener
邏輯一致,能夠了解一下以前的文章Redux:百行代碼千行文檔。createListenerCollection
經過閉包的方式存儲current
和next
,而後返回
{ clear, notify, subscribe }
做爲對外接口,分別用來清除當前存儲的listener、通知、訂閱,其目的就是實現一個監聽者模式。而後類Subscription
封裝了訂閱的邏輯,Subscription
根據構造函數中是否傳入了父級的訂閱類Subscription實例parentSub
,訂閱方法trySubscribe
會有不一樣的行爲。首先看看parentSub
的來源:
//this.propsMode來自於constructor中的this.propsMode = Boolean(props[storeKey]),storeKey默認爲`store` const parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey]
咱們知道Provider
的主要做用就是經過context
向子組件提供store
,而在conectAdvanced
函數的參數connectOptions
中的storeKey
是用來區分從context和props得到store的key值,只有在須要多個store的狀況下才會用到,固然這並非什麼好事,畢竟Redux追求的是單個store
。例如你設置了storeKey
爲otherStore
,那麼就能夠經過給wrapWithConnect
返回的組件添加屬性otherStore
,從而注入新的store
。
下面咱們區分幾種狀況:
狀況1:
若是Provider
中的子組件鏈接到Redux的store,而且祖先組件都沒有鏈接到Redux的store,也就是說是當前組件是通往根節點的路徑中第一個鏈接到Redux的store的組件,這時候直接可使用Redux的store
中的subscribe
方法去訂閱store
的改變。對應於的代碼是tryUnsubscribe
方法中的
this.store.subscribe(this.onStateChange)。
狀況2:
若是當前組件並非通往根節點的路徑中第一個鏈接到Redux的store的組件,也就是父組件中存在已經鏈接到Redux的store的組件。這時候,必需要保證下層的組件響應store
改變的函數調用必須晚於父級組件響應store
的函數調用,例如在圖中紅色的組件在store更新時是晚於黑色的組件的。代碼中是以下實現的,在父級組件中,以下:
getChildContext() { const subscription = this.propsMode ? null : this.subscription return { [subscriptionKey]: subscription || this.context[subscriptionKey] } }
所以在子組件(紅色)中就能夠經過context
得到父組件的subscription(也就是parentSub)。這樣在執行tryUnsubscribe
時對應於
this.parentSub.addNestedSub(this.onStateChange)
這樣咱們將子組件處理store中state的函數添加到parentSub
中的listener
中。這樣在父組件更新結束後,就能夠調用this.notifyNestedSubs()
。這樣就保證了更新順序,子組件永遠在父組件更新以後。
狀況3:
如上圖所示,右邊的組件是經過屬性prop的方式傳入了store
,那麼組件中的this.store
中的值就是經過以props傳入的store
。假如祖先元素沒有鏈接到store
的組件,那麼當前組件中parentSub
值就爲空。因此訂閱的方式就是以props中的store
:
this.store.subscribe(this.onStateChange)。
狀況4:
如上圖所示,右下方的組件的父組件(紫色)是經過props傳入store
的,那麼在父組件(紫色)中有
getChildContext() { const subscription = this.propsMode ? null : this.subscription return { [subscriptionKey]: subscription || this.context[subscriptionKey] } }
父組件對子組件暴露context
,其中context
中的subscriptionKey
屬性值爲this.context[subscriptionKey]
,要麼是null
,要麼是祖先元素中非props
方式傳入store
的組件的subscription
。也就是說以props傳入的store
的父組件不會影響子組件的訂閱store
。感受說的太過於抽象,咱們舉個例子:
在上面這個例子中,若是發出dispatch
更新store1,組件A和組件C都會刷新,組件B不會刷新。
討論了這麼多,咱們能夠看一下initSubscription
的實現方式:
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.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription) }
若是當前的store不是以props的方式傳入的,那麼parentSub
是this.context[subscriptionKey]
。若是是以props的方式傳入的,若顯式地給組件以props的方式傳入subscription時,parentSub
值爲this.props.subscription
。須要注意的是,咱們在initSubscription
中拷貝了當前this.subscription
中的notifyNestedSubs
方法,目的是防止在notify
循環過程當中組件卸載,使得this.subscription
爲null
。咱們在組件卸載時,會將值賦值爲一個名爲no-loop
的空函數,避免出錯。固然這並非惟一的解決方法。
接下咱們能夠看一下Connect
組件中主要生命週期函數:
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 }
組件在did mount
時會根據可選參數shouldHandleStateChanges
選擇是否訂閱store
的state
改變。組件在接受props時,會使用selector計算新的props並執行相應的聲明週期。shouldComponentUpdate
會根據this.selector
存儲的值shouldComponentUpdate
來判斷是否須要刷新組件。在組件will mount
時會作相應的清理,防止內存泄露。
接着咱們介紹其餘的類方法:
getWrappedInstance() { return this.wrappedInstance } setWrappedInstance(ref) { this.wrappedInstance = ref }
getWrappedInstance
與setWrappedInstance
在可選參數withRef
爲true時,獲取或者存儲被包裹組件的實例(ref)。
onStateChange() { this.selector.run(this.props) if (!this.selector.shouldComponentUpdate) { this.notifyNestedSubs() } else { this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate this.setState(dummyState)//dummyState === {} } } notifyNestedSubsOnComponentDidUpdate() { this.componentDidUpdate = undefined this.notifyNestedSubs() }
onStateChange
函數是store發生改變的回調函數,當回調onStateChange
方法時,會經過selector計算新的props,若是計算selcetor的結果中shouldComponentUpdate
爲false
,表示不須要刷新當前組件僅須要通知子組件更新。若是shouldComponentUpdate
爲true
,會經過設置this.setState({})
來刷新組件,並使得在組件更新結束以後,通知子組件更新。
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 }
addExtraProps
函數主要用做爲selector
計算出的props
增長新的屬性。例如,ref
屬性用來綁定回調存儲組件實例的函數setWrappedInstance
,renderCountProp
爲當前組件屬性刷新的次數,subscriptionKey
用來傳遞當前connect
中的subscription
。
render() { const selector = this.selector selector.shouldComponentUpdate = false if (selector.error) { throw selector.error } else { return createElement(WrappedComponent, this.addExtraProps(selector.props)) } }
render
函數其實就是高階函數中的屬性代理,首先將shouldComponentUpdate
置回false
,而後根據selector
中的計算過程是否存在error
,若是存在error
就拋出,不然執行
createElement(WrappedComponent, this.addExtraProps(selector.props))
若是你對上面語句不太熟悉,其實上面代碼等同於:
return ( <WrappedComponent {...this.addExtraProps(selector.props)} /> )
其實所謂的jsx
也無非是createElement
語法糖,全部的jsx
的語法都會被編譯成React.createElement
,因此哪怕你的代碼中沒有顯式的用到React
,只要有jsx
語法,就必須存在React
。
if (process.env.NODE_ENV !== 'production') { Connect.prototype.componentWillUpdate = function componentWillUpdate() { // We are hot reloading! if (this.version !== version) { this.version = version this.initSelector() if (this.subscription) this.subscription.tryUnsubscribe() this.initSubscription() if (shouldHandleStateChanges) this.subscription.trySubscribe() } } }
React-Redux在生產環境下是不支持熱重載的,只有在開發環境下提供這個功能。在開發環境中,組件在will update
時會根據this.version
與version
去判斷,若是二者不同,則初始化selector
,取消以前的訂閱並從新訂閱新的subscription
。
import { Component, Children } from 'react' import PropTypes from 'prop-types' import { storeShape, subscriptionShape } from '../utils/PropTypes' import warning from '../utils/warning' let didWarnAboutReceivingStore = false function warnAboutReceivingStore() { if (didWarnAboutReceivingStore) { return } didWarnAboutReceivingStore = true warning( '<Provider> does not support changing `store` on the fly. ' + 'It is most likely that you see this error because you updated to ' + 'Redux 2.x and React Redux 2.x which no longer hot reload reducers ' + 'automatically. See https://github.com/reactjs/react-redux/releases/' + 'tag/v2.0.0 for the migration instructions.' ) } export function createProvider(storeKey = 'store', subKey) { const subscriptionKey = subKey || `${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) } } if (process.env.NODE_ENV !== 'production') { Provider.prototype.componentWillReceiveProps = function (nextProps) { if (this[storeKey] !== nextProps.store) { warnAboutReceivingStore() } } } Provider.propTypes = { store: storeShape.isRequired, children: PropTypes.element.isRequired, } Provider.childContextTypes = { [storeKey]: storeShape.isRequired, [subscriptionKey]: subscriptionShape, } Provider.displayName = 'Provider' return Provider } export default createProvider()
首先咱們看看函數createProvider
,createProvider
函數的主要做用就是定製Provider
,咱們知道Provider
的主要做用是使得其全部子組件能夠經過context
訪問到Redux的store
。咱們看到createProvider
返回了類Provider
,而類Provider
的getChildContext
函數返回了{ [storeKey]: this[storeKey], [subscriptionKey]: null }
,使得全部子組件都能訪問到store
。須要注意的是,要想使得子組件訪問到context
必須同時定義兩點:getChildContext
函數與static childContextTypes = {}
。而且咱們知道Redux 2.x 與React-Redux 2.x再也不支持熱重載的reducer
,因此在非生產環境下,咱們會爲Provider
添加生命週期函數componentWillReceiveProps
,若是store
的值發生了變化,就會在提供警告提示。
Provider
的render
函數中返回了Children.only(this.props.children)
。Children
是React
提供的處理組件中this.props.children
的工具包(utilities)返回僅有的一個子元素,不然(沒有子元素或超過一個子元素)報錯且不渲染任何東西。因此Provider
僅支持單個子組件。
最後歡迎你們關注個人掘金帳號或者博客,不足之處,歡迎指正。