Redux著眼于對狀態(tài)整理的維護(hù)餐蔬,而不會產(chǎn)生出具體變動的部分缔莲。React是一個由狀態(tài)整體輸出界面的view層實現(xiàn)骡技,因此二者可以說是絕配钾菊。
如何在React項目中使用Redux
當(dāng)我們在說如何使用Redux時溃槐,說的其實是如何獲取并使用store內(nèi)容(狀態(tài)數(shù)據(jù))匣砖,以及創(chuàng)建并觸發(fā)action的過程。
一. 獲取并使用store
- 屬性傳遞store狀態(tài)數(shù)據(jù)
Redux的特點是整個應(yīng)用的狀態(tài)信息保存在一個store中昏滴,因而需要由store將數(shù)據(jù)從React組件樹節(jié)點傳入猴鲫。為了在數(shù)據(jù)變化時更新界面,還需要對store進(jìn)行監(jiān)聽:
function render() {
const state = store.getState();
React.render(
<App state={state}/>,
document.getElementById('app')
);
}
store.subscribe(render);
render();
這樣谣殊,在組件App中拂共,可以通過this.props.state獲取狀態(tài)信息。組件App可以進(jìn)一步將狀態(tài)信息以props的形式傳遞給其子孫節(jié)點:
class App extends React.Component {
render() {
return (
<Header content={this.props.title}/>
);
}
}
正如例子中的組件Header姻几,每個組件都可以從上層節(jié)點獲取它所需的信息宜狐。
然而势告,當(dāng)項目規(guī)模變大,組件層級變多時抚恒,情況會急劇惡化咱台,因為如果某個組件處在組件數(shù)第n級,那么沒修改一個屬性或增加一個組件俭驮,都需要改動n-1處回溺,這是無法想象的維護(hù)成本。
下面是很自然思考到的解決這個問題的辦法
- 組件自行獲取store狀態(tài)數(shù)據(jù)
對應(yīng)的的做法是混萝,把createStore的結(jié)果通過一個獨立的模塊以module export的方式暴露出來遗遵,所有組件都可以直接去import這個模塊得到store,然后對store進(jìn)行subscribe:
// store.js
import reducers from 'reducers';
export default createStore(reducers);
// TodoFilter.js
import store from 'store';
export default class TodoFilter extends React.Component {
constructor() {
super();
this.state = {
filter: null
};
}
componentDidMount() {
store.subscribe(() => {
this.setState({
filter: store.getState().filter
});
});
}
render() {
// 界面渲染
}
}
在組件掛載完成后監(jiān)聽store的變化逸嘀,取出最新狀態(tài)數(shù)據(jù)车要,并通過組件自身的setState方法更新組件的state信息,自動觸發(fā)重新渲染組件界面崭倘。
通過這種方式翼岁,我們順利地解決了前面遇到的問題,但依然存在一些問題:
- 組件私自與store建立聯(lián)系绳姨,致數(shù)據(jù)流難以追溯登澜。
- 擁有內(nèi)部state的組件不便于測試阔挠。
- 每個需要訪問store組件都實現(xiàn)一份subscript&setState這樣的邏輯略顯繁瑣飘庄。
react-redux將是上面這些問題較為成熟的、通用的解決方案购撼。
二. 創(chuàng)建與觸發(fā)action
在Flux架構(gòu)中跪削,store的狀態(tài)改變必須由action引起,除了獲取與使用store狀態(tài)數(shù)據(jù)外迂求,另外一個與store打交道的方式便是創(chuàng)建與觸發(fā)action碾盐。在Redux中,這個過程包含兩個部分:
- 創(chuàng)建action揩局,使用actionCreator
- 觸發(fā)action毫玖,通過store.dispatch將action作用到特定的store上
不難發(fā)現(xiàn)脱吱,觸發(fā)action的方法dispatch村斟,也面臨著一樣的問題,就是獲取store狗热。下面介紹針對這一問題的官方答案:react-redux驰怎。
react-redux
react-redux是Redux官方提供的React綁定阐滩,用于輔助在React項目中使用Redux,性能優(yōu)異且靈活強大县忌。API相當(dāng)簡單掂榔,包括一個Provider組件和一個connect高階方法继效。
- Provider
Provider是store提供者。只要把組件樹根節(jié)點包裹在Provider中装获,整個組件樹上的節(jié)點都可以通過connect獲取store:
ReactDOM.render(
<Provider store={store}>
<MyRootComponent />
</Provider>
);
- connect
connect是用來“連接”store與組件的方法瑞信,用法如下:
import { add } from 'actions';
function mapStateToProps(state) {
return {
num: state.num
};
}
function mapDispatchToProps(dispatch) {
return {
onBtnClick() {
dispatch(add()); // add()為action creator
}
};
}
function Counter(props) {
return (
<p>
{props.num}
<button onClick={props.onBtnClick}>+1</button>
</p>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
在這個示例中,我們通過connect讓組件Counter得以連接store穴豫,從store中取得num信息并在按鈕單擊時觸發(fā)store上的add方法喧伞。
- enhancer
connect()的執(zhí)行結(jié)果是一個“高階組件”,我們稱之為enhancer绩郎。
高階組件指符合以下條件的函數(shù):接收一個已有的組件作為參數(shù)潘鲫,返回一個新組件,后者將前者封裝于內(nèi)部肋杖。一般使用高階組件都是為了對已有組件進(jìn)行某些能力上的增強溉仑。
這里enhancer對傳入組件進(jìn)行怎樣的增強呢?答案便是接觸store的能力状植。
原有組件Counter不直接與store打交道浊竟,甚至不知道store與Redux的存在,而經(jīng)過enhancer處理得到的組件津畸,即上例中最終被export的內(nèi)容振定,能夠直接接觸到store,監(jiān)聽肉拓、讀取狀態(tài)數(shù)據(jù)并觸發(fā)action后频。這里的store,就是先前通過Provider引入的暖途。
react-redux通過React在0.1.4版引入的context特性實現(xiàn)了store內(nèi)容的隱式傳遞:Provider作為整個組件樹的根節(jié)點卑惜,通過實現(xiàn)getChildContext方法將store提供給它的的子孫們,而enhancer通過組件的context屬性獲取store對象驻售,從而可以調(diào)用其提供的subscript露久、getState、dispatch等方法欺栗。