現實的狀態跟 Reducer 通常都有幾個以上,我們來看一下多個 Reducer 的例子
多個(multiple) Reducer
const login = (state = false, action) => {
switch (action.type) {
case ‘LOGIN’:
return true;
case ‘LOGOUT’:
return false;
default:
return state;
}
}
const messages = (state = [], action) => {
switch (action.type) {
case ‘ADD_MESSAGE’:
return […state, action.payload];
case ‘REMOVE_MESSAGE’:
return state.filter(msg => msg.id
!= action.payload);
default:
return state;
}
}
const myReducer = {login, messages}; // object of two reducer functions
承前面的例子我們再加上一個簡單的 login reducer, 這個 reducer 狀態很單純,是一個布林值 (boolean) 表示使用者登入狀態,這時候的 myReducer 是一個物件,它的內容 key 是 login 跟 messages,value 是兩個函數 ((state, action) => state),而狀態樹會是
inteface State {
login: boolean;
messages: message[];
}
interface message {
id: number;
msg: string;
}
結合多個 Reducer
我們用下列的程式來應用這個多重的 Reducer 到這個多重的狀態樹
const combineReducer = reducers => (state, action) => {
return Object.keys(reducers).reduce((nextState, key) => {
nextState[key]=reducers[key](state[key], action);
return nextState;
}, {})
}
const rootReducer = combineReducer(myReducer);
稍微說明一下
Object.keys(reducers) 是取用 Reducers(myReducer) 這個 Object 的 keys, 也就是 login 跟 message 在這個例子中。
我們再次看到 .reduce() 這個 Operator, 其實可以將它當成 for-loop,差別在於他會累積結果,也就是累積新的狀態,initial 從 {} 開始。
第一個 key 是 login, 所以它會取 state[key] 也就是 login 的狀態,加上 action應用於 reducers[key] 就是 login reducer 的函數 ((state, action) => state),產生新的 login nextState。.reduce() 將這個新的狀態加入 {}。
第二個 key 是 message, 會用 state[key] 也就是 messages 的舊狀態加入 action 應用 reducer[key] 就是 messages reducer 的函數 ((state, action) => state), 產生 messages 新的狀態,.reduce() 將這個新的狀態加入上面的物件。
.reduce() 將新生的狀態一個一個加入 {},產生新的狀態樹。
看到這裡,大家大概明瞭為什麼叫它 Reducer 了,在一個更複雜的狀態樹中,Reducer 負責將一個個狀態應用到對應的函數中,再將它們包成一個狀態樹
將 Reducer 加入 Store
有了 Reducer, 我們在將它加入 Store 的架構中,就成了完整的 Store
interface Action {
type: string;
payload?: any
}
class Dispatcher extends Rx.Subject<Action> {
dispatch(act) {
this.next(act);
}
}
class Store extends Rx.BehaviorSubject<Action> {
constructor(private dispatcher, private reducer, initialState) {
super(initialState);
this.dispatcher
.do((v) => { /*console.log(‘do some effect for’, v.type) */ })
.scan((s, v) => this.reducer(s, v), initialState)
.subscribe(state => {
super.next(state); // new state, push to subscriber
});
}
dispatch(act) { // delegate to dispatcher
this.dispatcher.dispatch(act);
}
// override next to allow store subscribe action$
next(act) {
this.dispatcher.dispatch(act);
}
}
const login = (state = false, action) => {
switch (action.type) {
case ‘LOGIN’:
return true;
case ‘LOGOUT’:
return false;
default:
return state;
}
}
const messages = (state = [], action) => {
switch (action.type) {
case ‘ADD_MESSAGE’:
return […state, action.payload];
case ‘REMOVE_MESSAGE’:
return state.filter(msg => msg.id
!= action.payload);
default:
return state;
}
}
const myReducer = {login, messages}; // object of two reducer functions
const combineReducer = reducers => (state, action) => {
return Object.keys(reducers).reduce((nextState, key) => {
nextState[key]=reducers[key](state[key], action);
//console.log(‘nextState is ‘, nextState);
return nextState;
}, {})
}
const rootReducer = combineReducer(myReducer);
// instanciate new store
const initialState = {login: false, messages: []};
const dispatcher = new Dispatcher();
const store = new Store(dispatcher, rootReducer, initialState);
// add subscriber
const sub1 = store.subscribe(s => console.log(‘state ===> ‘, s));
// start dispatch action
store.dispatch({type: ‘LOGIN’}); // dispatch new action to store
store.dispatch({type: ‘LOGOUT’}); // dispatch another action
store.dispatch({type: ‘ADD_MESSAGE’, payload: {id: 1, msg: ‘First Message’}});
store.dispatch({type: ‘ADD_MESSAGE’, payload: {id: 2, msg: ‘Second Message’}});
store.dispatch({type: ‘REMOVE_MESSAGE’, payload: 1});
codepen
部分輸出如下
转载请注明:XAMPP中文组官网 » Store 加完整的 Reducer