React 實踐心得:react-redux 之 connect 方法詳解

ogrd2468 8年前發布 | 27K 次閱讀 Redux Web框架

Redux 是「React 全家桶」中極為重要的一員,它試圖為 React 應用提供「可預測化的狀態管理」機制。Redux 本身足夠簡單,除了 React,它還能夠支持其他界面框架。所以如果要將 Redux 和 React 結合起來使用,就還需要一些額外的工具,其中最重要的莫過于 react-redux 了。

react-redux 提供了兩個重要的對象, Provider 和 connect ,前者使 React 組件可被連接(connectable),后者把 React 組件和 Redux 的 store 真正連接起來。react-redux 的文檔中,對 connect 的描述是一段晦澀難懂的英文,在初學 redux 的時候,我對著這段文檔閱讀了很久,都沒有全部弄明白其中的意思(大概就是,單詞我都認識,連起來啥意思就不明白了的感覺吧)。

在使用了一段時間 redux 后,本文嘗試再次回到這里,給 這段文檔 (同時摘抄在附錄中)一個靠譜的解讀。

預備知識

首先回顧一下 redux 的基本用法。如果你還沒有閱讀過 redux 的文檔,你一定要先去 閱讀一下

const reducer = (state = {count: 0}, action) => {
  switch (action.type){
    case 'INCREASE': return {count: state.count + 1};
    case 'DECREASE': return {count: state.count - 1};
    default: return state;
  }
}

const actions = {
  increase: () => ({type: 'INCREASE'}),
  decrease: () => ({type: 'DECREASE'})
}

const store = createStore(reducer);

store.subscribe(() =>
  console.log(store.getState())
);

store.dispatch(actions.increase()) // {count: 1}
store.dispatch(actions.increase()) // {count: 2}
store.dispatch(actions.increase()) // {count: 3}

通過 reducer 創建一個 store ,每當我們在 store 上 dispatch 一個 action , store 內的數據就會相應地發生變化。

我們當然可以 直接 在 React 中使用 Redux:在最外層容器組件中初始化 store ,然后將 state 上的屬性作為 props 層層傳遞下去。

class App extends Component{

  componentWillMount(){
    store.subscribe((state)=>this.setState(state))
  }

  render(){
    return <Comp state={this.state}
 onIncrease={()=>store.dispatch(actions.increase())}
 onDecrease={()=>store.dispatch(actions.decrease())}
 />
 }
}

但這并不是最佳的方式。最佳的方式是使用 react-redux 提供的 Provider 和 connect 方法。

使用 react-redux

首先在最外層容器中,把所有內容包裹在 Provider 組件中,將之前創建的 store 作為 prop 傳給 Provider 。

const App = () => {
  return (
    <Provider store={store}>
 <Comp/>
 </Provider>
  )
};

Provider 內的任何一個組件(比如這里的 Comp ),如果需要使用 state 中的數據,就必須是「被 connect 過的」組件——使用 connect 方法對「你編寫的組件( MyComp )」進行包裝后的產物。

class MyComp extends Component {
  // content...
}

const Comp = connect(...args)(MyComp);

可見, connect 方法是重中之重。

connect 詳解

究竟 connect 方法到底做了什么,我們來一探究竟。

首先看下函數的簽名:

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

connect() 接收四個參數,它們分別是 mapStateToProps , mapDispatchToProps , mergeProps 和 options 。

mapStateToProps(state, ownProps) : stateProps

這個函數允許我們將 store 中的數據作為 props 綁定到組件上。

const mapStateToProps = (state) => {
  return {
    count: state.count
  }
}

這個函數的第一個參數就是 Redux 的 store ,我們從中摘取了 count 屬性。因為返回了具有 count 屬性的對象,所以 MyComp 會有名為 count 的 props 字段。

class MyComp extends Component {
  render(){
    return <div>計數:{this.props.count}次</div>
  }
}

const Comp = connect(...args)(MyComp);

當然,你不必將 state 中的數據原封不動地傳入組件,可以根據 state 中的數據,動態地輸出組件需要的(最小)屬性。

const mapStateToProps = (state) => {
  return {
    greaterThanFive: state.count > 5
  }
}

函數的第二個參數 ownProps ,是 MyComp 自己的 props 。有的時候, ownProps 也會對其產生影響。比如,當你在 store 中維護了一個用戶列表,而你的組件 MyComp 只關心一個用戶(通過 props 中的 userId 體現)。

const mapStateToProps = (state, ownProps) => {
  // state 是 {userList: [{id: 0, name: '王二'}]}
  return {
    user: _.find(state.userList, {id: ownProps.userId})
  }
}

class MyComp extends Component {

  static PropTypes = {
    userId: PropTypes.string.isRequired,
    user: PropTypes.object
  };

  render(){
    return <div>用戶名:{this.props.user.name}</div>
  }
}

const Comp = connect(mapStateToProps)(MyComp);

當 state 變化,或者 ownProps 變化的時候, mapStateToProps 都會被調用,計算出一個新的 stateProps ,(在與 ownProps merge 后)更新給 MyComp 。

這就是將 Redux store 中的數據連接到組件的基本方式。

mapDispatchToProps(dispatch, ownProps): dispatchProps

connect 的第二個參數是 mapDispatchToProps ,它的功能是,將 action 作為 props 綁定到 MyComp 上。

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    increase: (...args) => dispatch(actions.increase(...args)),
    decrease: (...args) => dispatch(actions.decrease(...args))
  }
}

class MyComp extends Component {
  render(){
    const {count, increase, decrease} = this.props;
    return (<div>
 <div>計數:{this.props.count}次</div>
 <button onClick={increase}>增加</button>
 <button onClick={decrease}>減少</button>
 </div>)
  }
}

const Comp = connect(mapStateToProps, mapDispatchToProps)(MyComp);

由于 mapDispatchToProps 方法返回了具有 increase 屬性和 decrease 屬性的對象,這兩個屬性也會成為 MyComp 的 props 。

如上所示,調用 actions.increase() 只能得到一個 action 對象 {type:'INCREASE'} ,要觸發這個 action 必須在 store 上調用 dispatch 方法。 diapatch 正是 mapDispatchToProps 的第一個參數。但是,為了不讓 MyComp 組件感知到 dispatch 的存在,我們需要將 increase 和 decrease 兩個函數包裝一下,使之成為直接可被調用的函數(即,調用該方法就會觸發 dispatch )。

Redux 本身提供了 bindActionCreators 函數,來將 action 包裝成直接可被調用的函數。

import {bindActionCreators} from 'redux';

const mapDispatchToProps = (dispatch, ownProps) => {
  return bindActionCreators({
    increase: action.increase,
    decrease: action.decrease
  });
}

同樣,當 ownProps 變化的時候,該函數也會被調用,生成一個新的 dispatchProps ,(在與 statePrope 和 ownProps merge 后)更新給 MyComp 。注意, action 的變化不會引起上述過程,默認 action 在組件的生命周期中是固定的。

[mergeProps(stateProps, dispatchProps, ownProps): props]

之前說過,不管是 stateProps 還是 dispatchProps ,都需要和 ownProps merge 之后才會被賦給 MyComp 。 connect 的第三個參數就是用來做這件事。通常情況下,你可以不傳這個參數, connect 就會使用 Object.assign 替代該方法。

其他

最后還有一個 options 選項,比較簡單,基本上也不大會用到(尤其是你遵循了其他的一些 React 的「最佳實踐」的時候),本文就略過了。希望了解的同學可以直接看文檔。

(完)

 

來自:http://taobaofed.org/blog/2016/08/18/react-redux-connect/

 

Save

 本文由用戶 ogrd2468 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!