首先,一張 Redux 解釋圖鎮樓:javascript
const store = createStore(reducer);
store 是一個對象,包含3個方法:getState
、dispatch
、subscribe
html
// createStore 函數實現 const createStore = (reducer) => { let state; let listeners = []; const getState = () => state; const dispatch = (action) => { state = reducer(state, action); listeners.forEach(listener => listener()); } const subscribe = (listener) => { // listener 就是一個要執行的函數 listeners.push(listener); return () => { // 採用柯里化方式註銷監聽器,用法:store.subscribe(listener)(); listeners = listeners.filter(l => l != listener); } } dispatch({}); // 初始化state return { getState, dispatch, subscribe } }
由函數可知,當用戶
dispatch
一個 action 時,會自動調用reducer
從而獲得最新的 state,該 state 可經過getState
函數獲取,而且會執行全部已註冊的函數。java
因此,redux 的套路就是(參考 React小書 ):react
// 定一個 reducer function reducer (state, action) { /* 初始化 state 和 switch case */ } // 生成 store const store = createStore(reducer) // 監聽數據變化從新渲染頁面,即更新狀態的過程 store.subscribe(() => renderApp(store.getState())) // 首次渲染頁面 renderApp(store.getState()) // 後面能夠隨意 dispatch 了,頁面自動更新 store.dispatch(...)
從圖中能夠看到,Store 經過 Provider 傳遞給了咱們的 React 組件,所以,使得組件可以獲取到 store。那麼它是如何將作到的呢?git
爲了弄明白 React 和 Redux 之間是如何鏈接的,咱們須要瞭解如下一些內容(參考 React小書 ):github
在 React 中,父組件使用 getChildContext(),能夠將 store 放到它的 context 裏面,至關於給子組件設置了一個全局變量,這樣每一個子組件就均可以獲取到 store。redux
// 父組件 class Index extends Component { // 提供 context 的組件必須提供 childContextTypes 做爲 context 的聲明和驗證 static childContextTypes = { store: PropTypes.object } // 一個組件能夠經過 getChildContext 方法返回一個對象,這個對象就是子樹的 context getChildContext () { return { store } } render () { return ( <div> <Header /> <Content /> </div> ) } } // 子組件 class Header extends Component { // 聲明想要的 context 裏面的哪些狀態,以便經過 this.context 進行訪問 // 子組件要獲取 context 裏面的內容的話,就必須寫 contextTypes 來聲明和驗證你須要獲取的狀態的類型 static contextTypes = { store: PropTypes.object } constructor () { super() this.state = { themeColor: '' } } componentWillMount () { this._updateThemeColor() } _updateThemeColor () { // 子組件能夠訪問到父組件 context 裏面的內容 const { store } = this.context const state = store.getState() this.setState({ themeColor: state.themeColor }) } render () { return ( <h1 style={{ color: this.state.themeColor }}>React.js 小書</h1> ) } }
若是一個組件設置了 context,那麼它的子組件均可以直接訪問到裏面的內容,它就像這個組件爲根的子樹的全局變量。任意深度的子組件均可以經過 contextTypes 來聲明你想要的 context 裏面的哪些狀態,而後能夠經過 this.context 訪問到那些狀態。性能優化
context 存在的問題:首先,它是一個試驗性的API,不穩定,可能會改變,雖然好多庫都用到了這個特性;其次它是脆弱的,若是在層級中的任何一個組件執行了 shouldComponentUpdate 返回 false,context 則不會傳遞給其以後全部的子組件。app
由於 context 是一個比較危險的特性,咱們不想在本身寫組件的時候被其污染,咱們須要將其剝離出來,所以,react-redux 誕生了,其中的 Provider
以及 connect
就幫助咱們將 React 的組件和 Redux 的 store 進行了鏈接。ide
做用:充當父組件的做用,把 store 放到本身的 context 裏面,讓子組件 connect 的時候獲取。
export class Provider extends Component { static propTypes = { store: PropTypes.object, children: PropTypes.any } static childContextTypes = { store: PropTypes.object } getChildContext () { return { store: this.props.store } } render () { return ( <div>{this.props.children}</div> ) } }
高階組件:高階組件是一個接受一個組件爲參數,並返回一個被包裝過的組件的函數,即返回傳入props的原組件。
connect 的做用:和 React 的 context 打交道,將 context 中的數據取出來,並以 prop 的形式傳遞給 Dumb 組件。
const mapStateToProps = (state) => { themeColor: state.themeColor } const mapDispatchToProps = (dispatch) => ({ onSwitchColor(color) => { dispatch({ type: 'CHANGE_COLOR', themeColor: color }) } }); // connect 實現 // connect 接受 mapStateToProps 和 mapDispatchProps 參數後,返回的函數是高階組件,該高階組件接受一個組件做爲參數,而後用 Connect 包裝以後返回 export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => { class Connect extends Component { static contextTypes = { store: PropTypes.object } constructor () { super() this.state = { allProps: {} } } componentWillMount () { const { store } = this.context this._updateProps() store.subscribe(() => this._updateProps()) } _updateProps () { const { store } = this.context let stateProps = mapStateToProps ? mapStateToProps(store.getState(), this.props) : {} // 防止 mapStateToProps 沒有傳入 let dispatchProps = mapDispatchToProps ? mapDispatchToProps(store.dispatch, this.props) : {} // 防止 mapDispatchToProps 沒有傳入 this.setState({ allProps: { ...stateProps, ...dispatchProps, ...this.props } }) } render () { return <WrappedComponent {...this.state.allProps} /> } } return Connect }
connect 接口:
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])(Component)