React-redux深刻理解

首先,一張 Redux 解釋圖鎮樓:javascript

Redux explained(圖片來源:https://github.com/buckyroberts/React-Redux-Boilerplate)

【回顧】Redux 的核心: store 是什麼?(createStore 函數的實現)
const store = createStore(reducer);

store 是一個對象,包含3個方法:getStatedispatchsubscribehtml

// 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(...)
【問題】:React 和 Redux 之間是如何鏈接?

從圖中能夠看到,Store 經過 Provider 傳遞給了咱們的 React 組件,所以,使得組件可以獲取到 store。那麼它是如何將作到的呢?git

爲了弄明白 React 和 Redux 之間是如何鏈接的,咱們須要瞭解如下一些內容(參考 React小書 ):github

1、背景:React 中父組件 context 的做用,用以擺脫狀態提高

在 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

2、react-redux 的誕生

由於 context 是一個比較危險的特性,咱們不想在本身寫組件的時候被其污染,咱們須要將其剝離出來,所以,react-redux 誕生了,其中的 Provider 以及 connect 就幫助咱們將 React 的組件和 Redux 的 store 進行了鏈接。ide

1. Provider 的實現

做用:充當父組件的做用,把 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>
    )
  }
}
2. 高階組件 connect(connect 實現)

高階組件:高階組件是一個接受一個組件爲參數,並返回一個被包裝過的組件的函數,即返回傳入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)

3、react-redux 的性能優化

react-redux性能優化之reselect

3、文章參考

React小書
Redux使用小結

相關文章
相關標籤/搜索