React 實踐心得:react-redux 之 connect 要領詳解

轉載注:

  • 本文作者是淘寶前端團隊的恭弘=叶 恭弘齋。筆者異常喜好這篇文章,故從新排版並轉載到這裏,同時也加入了一些自身的體味。
  • 原文地點:http://taobaofed.org/blog/201…

Redux 是「React 百口桶」中極為主要的一員,它試圖為 React 運用供應「可展望化的狀況治理」機制。Redux 自身充足簡樸,除了 React,它還能夠支撐其他界面框架。所以假如要將 Redux 和 React 結合起來運用,就還須要一些分外的東西,个中最主要的莫過於 react-redux 了。

react-redux 供應了兩個主要的對象,Providerconnect,前者使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,每當我們在storedispatch一個actionstore內的數據就會響應地發生變化。

我們固然能夠直接在 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())}
      />
    )
  }
}

轉載注:

  • 另一種要領,在進口文件index.js中初始化store,並將其export出來,然後import到定義組件的文件中去。

但這並非最好的體式格局。最好的體式格局是運用 react-redux 供應的Providerconnect要領。

運用 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()吸收四個參數,它們分別是mapStateToPropsmapDispatchToPropsmergePropsoptions

mapStateToProps

mapStateToProps(state, ownProps) : stateProps

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

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

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

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中的數據銜接到組件的基礎體式格局。

轉載注:

  • 什麼叫做“MyComp自身的props”?假設在不運用 react-redux 的時刻,MyComp的父組件是ParentComp,那末上文中的ownPropsParentComp通報給MyComp的悉數屬性(關於下文中的要領mapDispatchToProps亦同)。也就是說,ownProps與 Redux 的storestate完整無關
  • 要領mapStateToPropsMyComp增添的屬性,不可能被要領mapDispatchToProps訪問到,反之亦然。因為,這涉及到 render 的機遇和遞次的題目,筆者在這上面踩過相當多的坑。至於筆者為何有這類需求,因為筆者設想了一個按鈕,功用是:在點擊后,依據當前的 state 計算出下一個 state,並更新。在閱歷了無數error今後,筆者終究意想到:react-redux 基礎就不是設想用來處理這類題目標。處理方案有兩種:一是,在設想 reducer 的時刻,就直接依據 state 更新;二是,導入全局store並運用 store.getState() 取得當前state,然後依據這個state舉行更新。
  • 別的,假如運用PropTypesMyComp做屬性範例搜檢,要領mapStateToProps和要領mapDispatchToPropsMyComp增添的屬性是存在的。

mapDispatchToProps

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屬性的對象,這兩個屬性也會成為MyCompprops

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

Redux 自身供應了bindActionCreators函數,來將action包裝成直接可被挪用的函數。

import {bindActionCreators} from 'redux';

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

一樣,當ownProps變化的時刻,該函數也會被挪用,天生一個新的dispatchProps,(在與statePropsownProps merge 后)更新給MyComp。注重,action的變化不會引发上述歷程,默許action在組件的生命周期中是牢固的。

轉載注:

  • 函數connect以至react-redux的中心在於:將 Redux 中 store 的 state 綁定到組件的屬性上,使得對 state 的修正能夠直接表現為組件表面的變動。因而,參數mapStateToProps是異常主要的,然則參數mapDispatchToProps則比較過剩——因為簡樸粗獷地導入全局 store 一樣能到達雷同的目標(事實上筆者就是這麼做的)。

mergeProps

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

之前說過,不管是stateProps照樣dispatchProps,都須要和ownProps merge 以後才會被賦給MyCompconnect的第三個參數就是用來做這件事。通常情況下,你能夠不傳這個參數,connect就會運用Object.assign替換該要領。

其他

末了另有一個options選項,比較簡樸,基礎上也不大會用到(尤其是你遵照了其他的一些 React 的「最好實踐」的時刻),本文就略過了。願望相識的同硯能夠直接看文檔。

(完)

    原文作者:Zack
    原文地址: https://segmentfault.com/a/1190000015042646
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞