redux中connect的使用(2)實踐心得-轉載

摘要: 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 的「最佳實踐」的時候),本文就略過了。希望了解的同學可以直接看文檔。

(完)

附:connect 方法的官方英文文檔

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

Connects a React component to a Redux store.

It does not modify the component class passed to it. Instead, it returns a new, connected component class, for you to use.

Arguments

[mapStateToProps(state, [ownProps]): stateProps] (Function): If specified, the component will subscribe to Redux store updates. Any time it updates, mapStateToProps will be called. Its result must be a plain object*, and it will be merged into the component’s props. If you omit it, the component will not be subscribed to the Redux store. If ownProps is specified as a second argument, its value will be the props passed to your component, and mapStateToProps will be additionally re-invoked whenever the component receives new props (e.g. if props received from a parent component have shallowly changed, and you use the ownProps argument, mapStateToProps is re-evaluated).

[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function): If an object is passed, each function inside it will be assumed to be a Redux action creator. An object with the same function names, but with every action creator wrapped into a dispatch call so they may be invoked directly, will be merged into the component’s props. If a function is passed, it will be given dispatch. It’s up to you to return an object that somehow uses dispatch to bind action creators in your own way. (Tip: you may use the bindActionCreators() helper from Redux.) If you omit it, the default implementation just injects dispatch into your component’s props. If ownProps is specified as a second argument, its value will be the props passed to your component, and mapDispatchToProps will be re-invoked whenever the component receives new props.

[mergeProps(stateProps, dispatchProps, ownProps): props] (Function): If specified, it is passed the result of mapStateToProps(), mapDispatchToProps(), and the parent props. The plain object you return from it will be passed as props to the wrapped component. You may specify this function to select a slice of the state based on props, or to bind action creators to a particular variable from props. If you omit it, Object.assign({}, ownProps, stateProps, dispatchProps) is used by default.

[options] (Object) If specified, further customizes the behavior of the connector.

[pure = true] (Boolean): If true, implements shouldComponentUpdate and shallowly compares the result of mergeProps, preventing unnecessary updates, assuming that the component is a “pure” component and does not rely on any input or state other than its props and the selected Redux store’s state. Defaults to true.
[withRef = false] (Boolean): If true, stores a ref to the wrapped component instance and makes it available via getWrappedInstance() method. Defaults to false.

轉載自:http://taobaofed.org/blog/2016/08/18/react-redux-connect/
作者: 葉齋

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內容