Action 很簡(jiǎn)單,就是一個(gè)單純的包含 { type, payload } 的對(duì)象,type
是一個(gè)常量用來(lái)標(biāo)示動(dòng)作類(lèi)型,payload 是這個(gè)動(dòng)作攜帶的數(shù)據(jù)。Action 需要通過(guò)
store.dispatch() 方法來(lái)發(fā)送。
比如一個(gè)最簡(jiǎn)單的 action:
{
type: 'ADD_TODO',
text: 'Build my first Redux app'
}
一般來(lái)說(shuō),會(huì)使用函數(shù)(Action Creators)來(lái)生成 action,這樣會(huì)有更大的靈活性,Action Creators 是一個(gè) pure function,它最后會(huì)返回一個(gè) action 對(duì)象:
function addTodo(text) {
return {
type: 'ADD_TODO',
text
}
}
所以現(xiàn)在要觸發(fā)一個(gè)動(dòng)作只要調(diào)用 dispatch: dispatch(addTodo(text))
稍后會(huì)講到如何拿到 store.dispatch
Reducer 用來(lái)處理 Action 觸發(fā)的對(duì)狀態(tài)樹(shù)的更改。
所以一個(gè) reducer 函數(shù)會(huì)接受 oldState 和 action 兩個(gè)參數(shù),返回一個(gè)新的
state:(oldState, action) => newState。一個(gè)簡(jiǎn)單的 reducer 可能類(lèi)似這樣:
const initialState = {
a: 'a',
b: 'b'
};
function someApp(state = initialState, action) {
switch (action.type) {
case 'CHANGE_A':
return { ...state, a: 'Modified a' };
case 'CHANGE_B':
return { ...state, b: action.payload };
default:
return state
}
}
值得注意的有兩點(diǎn):
oldState 而是返回一個(gè) newStateoldStateReducer 也是 pure function,這點(diǎn)非常重要,所以絕對(duì)不要在 reducer 里面做一些引入 side-effects 的事情,比如:
Data.now() Math.random()因?yàn)?Redux 里面只有一個(gè) Store,對(duì)應(yīng)一個(gè) State 狀態(tài),所以整個(gè) State 對(duì)象就是由一個(gè) reducer 函數(shù)管理,但是如果所有的狀態(tài)更改邏輯都放在這一個(gè) reducer 里面,顯然會(huì)變得越來(lái)越巨大,越來(lái)越難以維護(hù)。得益于純函數(shù)的實(shí)現(xiàn),我們只需要稍微變通一下,讓狀態(tài)樹(shù)上的每個(gè)字段都有一個(gè) reducer 函數(shù)來(lái)管理就可以拆分成很小的 reducer 了:
function someApp(state = {}, action) {
return {
a: reducerA(state.a, action),
b: reducerB(state.b, action)
};
}
對(duì)于 reducerA 和 reducerB 來(lái)說(shuō),他們依然是形如:(oldState, action) => newState 的函數(shù),只是這時(shí)候的 state 不是整個(gè)狀態(tài)樹(shù),而是樹(shù)上的特定字段,每個(gè) reducer 只需要判斷 action,管理自己關(guān)心的狀態(tài)字段數(shù)據(jù)就好了。
Redux 提供了一個(gè)工具函數(shù) combineReducers 來(lái)簡(jiǎn)化這種 reducer 合并:
import { combineReducers } from 'redux';
const someApp = combineReducers({
a: reducerA,
b: reducerB
});
如果 reducer 函數(shù)名字和字段名字相同,利用 ES6 的 Destructuring
可以進(jìn)一步簡(jiǎn)化成:combineReducers({ a, b })
象 someApp 這種管理整個(gè) State 的 reducer,可以稱(chēng)為 root reducer。
現(xiàn)在有了 Action 和 Reducer,Store 的作用就是連接這兩者,Store 的作用有這么幾個(gè):
getState() 方法獲取 Statedispatch() 方法發(fā)送 action 更改 Statesubscribe() 方法注冊(cè)回調(diào)函數(shù)監(jiān)聽(tīng) State 的更改創(chuàng)建一個(gè) Store 很容易,將 root reducer 函數(shù)傳遞給 createStore 方法即可:
import { createStore } from 'redux';
import someApp from './reducers';
let store = createStore(someApp);
// 你也可以額外指定一個(gè)初始 State(initialState),這對(duì)于服務(wù)端渲染很有用
// let store = createStore(someApp, window.STATE_FROM_SERVER);
現(xiàn)在我們就拿到了 store.dispatch,可以用來(lái)分發(fā) action 了:
let unsubscribe = store.subscribe(() => console.log(store.getState()));
// Dispatch
store.dispatch({ type: 'CHANGE_A' });
store.dispatch({ type: 'CHANGE_B', payload: 'Modified b' });
// Stop listening to state updates
unsubscribe();
以上提到的 store.dispatch(action) -> reducer(state, action) -> store.getState() 其實(shí)就構(gòu)成了一個(gè)“單向數(shù)據(jù)流”,我們?cè)賮?lái)總結(jié)一下。
1. 調(diào)用 store.dispatch(action)
Action 是一個(gè)包含 { type, payload } 的對(duì)象,它描述了“發(fā)生了什么”,比如:
{ type: 'LIKE_ARTICLE', articleID: 42 }
{ type: 'FETCH_USER_SUCCESS', response: { id: 3, name: 'Mary' } }
{ type: 'ADD_TODO', text: 'Read the Redux docs.' }
你可以在任何地方調(diào)用 store.dispatch(action),比如組件內(nèi)部,Ajax
回調(diào)函數(shù)里面等等。
2. Action 會(huì)觸發(fā)給 Store 指定的 root reducer
root reducer 會(huì)返回一個(gè)完整的狀態(tài)樹(shù),State 對(duì)象上的各個(gè)字段值可以由各自的 reducer 函數(shù)處理并返回新的值。
(state, action) 兩個(gè)參數(shù)action.type 然后處理對(duì)應(yīng)的 action.payload 數(shù)據(jù)來(lái)更新并返回一個(gè)新的 state3. Store 會(huì)保存 root reducer 返回的狀態(tài)樹(shù)
新的 State 會(huì)替代舊的 State,然后所有 store.subscribe(listener)
注冊(cè)的回調(diào)函數(shù)會(huì)被調(diào)用,在回調(diào)函數(shù)里面可以通過(guò) store.getState() 拿到新的
State。
這就是 Redux 的運(yùn)作流程,接下來(lái)看如何在 React 里面使用 Redux。