在redux——入門1里面,我簡要介紹了redux的核心概念,并舉了一個簡單的計數器demo的例子,用于展示在react中怎么使用redux。現在,我打算把這個簡單的demo變得復雜一點,引入react-redux和redux中一些其他的概念。
工具分享
在本篇正式開始之前,我想先分享一個用于快速構建react應用的腳手架工具,傳送門:https://github.com/facebookincubator/create-react-app
概念引入
我會在這個升級版的demo里面,引入三個東西,Provider(react-redux),combineReducers(redux),connect(react-redux),如果你想更深入的了解react-redux中的各個角色,這里有個很好的解釋https://github.com/jasonslyvia/a-cartoon-intro-to-redux-cn
Provider
這是一個組件,沒有其他特殊的作用,但是我們需要將它包裹在整個組件樹的最外層,只有這樣,其內部的子孫組件才能使用connect來綁定store。
connect
這是一個函數,由react-redux提供,其返回依然是一個函數,該函數會處理視圖與store綁定的細節,具體的使用方法后面會做介紹。
combineReducers
這也是一個函數。在上一章,我們的reducer是一個單一的函數,在處理類似于計數器這樣的簡單應用時,我們不會看出有什么問題,但是當整個系統變得復雜后,單一的reducer就會變得臃腫不堪,所以我們需要對reducer進行分片,每一個reducer用于單獨處理一部分state,而combineReducers就是將分片的reducer合并為一個整體,這個函數的實現也比較簡單。
counter升級版
因為參照了react的官方示例,因此整個例子所使用的語法和上個簡化版的例子會有比較大的出入。
入口文件index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from 'redux';
import {Provider} from 'react-redux';
import reducer from './reducers';
import Root from './components/root';
let store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<Root></Root>
</Provider>,
document.getElementById('root')
);
同簡化版本相比,這里我們引入了Provider,并將其包裹在了組件的最外層。
reducers/index.js
import {combineReducers} from 'redux';
import counter from './counter';
const all = combineReducers({
count: counter
});
export default all;
在這里,我們調用了combineReducers方法,將counter這個reducer合并到主reducer上,因為計數器這個demo很簡單,所以我們只將state劃分了一個屬性count,而counter這個reducer和簡化版的沒有什么區別,姑且還是貼一下代碼
export default (state=0, action) => {
switch(action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
前面說過,combineReducer的實現其實是蠻簡單的,其實就是返回一個函數,每次處理action的時候,這個函數會遍歷所有的reducer來處理這個action,然后將所有結果打包返回,代碼和下面類似
// combineReducers
const combineReducers = ( reducers ) => {
return ( state = {}, action ) => {
return Object.keys(reducers).reduce(
( nextState, key ) => {
nextState[key] = reducers[key](
state[key],
action
);
return nextState;
},
{}
);
};
};
components/root.js
這是我們的根組件
import React from 'react';
import InputBox from '../containers/input-box';
import ShowBox from '../containers/show-box';
export default () => {
return (
<div>
<InputBox></InputBox>
<ShowBox></ShowBox>
</div>
);
};
這個沒什么可以講的,主要看接下來的InputBox和ShowBox
containers/input-box
這是一個純輸入組件
import React from 'react';
import {connect} from 'react-redux';
let InputBox = ({onIncrement, onDecrement}) => {
return (
<div>
<button type="button" onClick={onIncrement}>+++</button>
<button type="button" onClick={onDecrement}>---</button>
</div>
);
};
let mapDispatchToProps = (dispatch) => ({
onIncrement: () => dispatch({type: 'INCREMENT'}),
onDecrement: () => dispatch({type: 'DECREMENT'})
});
InputBox = connect(undefined, mapDispatchToProps)(InputBox);
export default InputBox;
在這里我們調用了react-redux的connect方法,當然,如果之前完全沒接觸過connect函數,看這段代碼可能會有點頭疼,可以先移步這里connect的api文檔。我也可以簡單介紹下connect的使用方法,它支持四個參數,我這里只介紹前兩個,后兩個因為我并沒怎么用過,所以暫時不講。
第一個參數是mapStateToProps(state, [ownProps])
,用于選擇性的將state中的屬性注入到組件的props中,我在show-box中使用了這個參數,所以請看后面的代碼。
第二個參數是mapDispatchToProps(dispatch, [ownProps])
,用于將需要觸發dispatch的方法,注入到組建的props中,在上面的input-box中,我使用了這個參數,將onIncrement
和onDecrement
兩個用于dispatch的方法注入到了InputBox中。
container/show-box
這是一個純展示的組件
import React from 'react';
import {connect} from 'react-redux';
let ShowBox = ({count}) => {
return (
<div>
<p>{count}</p>
</div>
);
};
let mapStateToProps = (state) => ({
count: state.count
});
ShowBox = connect(mapStateToProps)(ShowBox);
export default ShowBox;
唯一需要注意的是用了mapStateToProps
總結
雖然是說是升級版,但也只是多引入了幾個東西,總體來說還是算簡單,只是概念多了,容易讓人糊涂,我被繞了一個上午,好在現在總算有點清醒了,所以記下這些東西,方便之后的回顧。