轉載注:
- 本文作者是淘寶前端團隊的恭弘=叶 恭弘齋。筆者異常喜好這篇文章,故從新排版並轉載到這裏,同時也加入了一些自身的體味。
- 原文地點:http://taobaofed.org/blog/201…
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())}
/>
)
}
}
轉載注:
- 另一種要領,在進口文件
index.js
中初始化store
,並將其export
出來,然後import
到定義組件的文件中去。
但這並非最好的體式格局。最好的體式格局是運用 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
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
中的數據銜接到組件的基礎體式格局。
轉載注:
- 什麼叫做“
MyComp
自身的props
”?假設在不運用 react-redux 的時刻,MyComp
的父組件是ParentComp
,那末上文中的ownProps
是ParentComp
通報給MyComp
的悉數屬性(關於下文中的要領mapDispatchToProps
亦同)。也就是說,ownProps
與 Redux 的store
與state
完整無關。- 要領
mapStateToProps
為MyComp
增添的屬性,不可能被要領mapDispatchToProps
訪問到,反之亦然。因為,這涉及到 render 的機遇和遞次的題目,筆者在這上面踩過相當多的坑。至於筆者為何有這類需求,因為筆者設想了一個按鈕,功用是:在點擊后,依據當前的 state 計算出下一個 state,並更新。在閱歷了無數error
今後,筆者終究意想到:react-redux 基礎就不是設想用來處理這類題目標。處理方案有兩種:一是,在設想 reducer 的時刻,就直接依據 state 更新;二是,導入全局store
並運用store.getState()
取得當前state
,然後依據這個state
舉行更新。- 別的,假如運用PropTypes對
MyComp
做屬性範例搜檢,要領mapStateToProps
和要領mapDispatchToProps
為MyComp
增添的屬性是存在的。
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
屬性的對象,這兩個屬性也會成為MyComp
的props
。
如上所示,挪用actions.increase()
只能獲得一個action
對象{type:'INCREASE'}
,要觸發這個action
必須在store
上挪用dispatch
要領。dispatch
恰是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
,(在與stateProps
和ownProps
merge 后)更新給MyComp
。注重,action
的變化不會引发上述歷程,默許action
在組件的生命周期中是牢固的。
轉載注:
- 函數
connect
以至react-redux
的中心在於:將 Redux 中 store 的 state 綁定到組件的屬性上,使得對 state 的修正能夠直接表現為組件表面的變動。因而,參數mapStateToProps
是異常主要的,然則參數mapDispatchToProps
則比較過剩——因為簡樸粗獷地導入全局 store 一樣能到達雷同的目標(事實上筆者就是這麼做的)。
mergeProps
[mergeProps(stateProps, dispatchProps, ownProps): props]
之前說過,不管是stateProps
照樣dispatchProps
,都須要和ownProps
merge 以後才會被賦給MyComp
。connect
的第三個參數就是用來做這件事。通常情況下,你能夠不傳這個參數,connect
就會運用Object.assign
替換該要領。
其他
末了另有一個options
選項,比較簡樸,基礎上也不大會用到(尤其是你遵照了其他的一些 React 的「最好實踐」的時刻),本文就略過了。願望相識的同硯能夠直接看文檔。
(完)