通過了這麼多節的優化,咱們有了一個很通用的 createStore
:html
function createStore (state, stateChanger) { const listeners = [] const subscribe = (listener) => listeners.push(listener) const getState = () => state const dispatch = (action) => { state = stateChanger(state, action) // 覆蓋原對象 listeners.forEach((listener) => listener()) } return { getState, dispatch, subscribe } }
它的使用方式是:app
let appState = { title: { text: 'React.js 小書', color: 'red', }, content: { text: 'React.js 小書內容', color: 'blue' } } function stateChanger (state, action) { switch (action.type) { case 'UPDATE_TITLE_TEXT': return { ...state, title: { ...state.title, text: action.text } } case 'UPDATE_TITLE_COLOR': return { ...state, title: { ...state.title, color: action.color } } default: return state } } const store = createStore(appState, stateChanger) ...
咱們再優化一下,其實 appState
和 stateChanger
能夠合併到一塊兒去:函數
function stateChanger (state, action) { if (!state) { return { title: { text: 'React.js 小書', color: 'red', }, content: { text: 'React.js 小書內容', color: 'blue' } } } switch (action.type) { case 'UPDATE_TITLE_TEXT': return { ...state, title: { ...state.title, text: action.text } } case 'UPDATE_TITLE_COLOR': return { ...state, title: { ...state.title, color: action.color } } default: return state } }
stateChanger
如今既充當了獲取初始化數據的功能,也充當了生成更新數據的功能。若是有傳入 state
就生成更新數據,不然就是初始化數據。這樣咱們能夠優化 createStore
成一個參數,由於 state
和 stateChanger
合併到一塊兒了:性能
function createStore (stateChanger) { let state = null const listeners = [] const subscribe = (listener) => listeners.push(listener) const getState = () => state const dispatch = (action) => { state = stateChanger(state, action) listeners.forEach((listener) => listener()) } dispatch({}) // 初始化 state return { getState, dispatch, subscribe } }
createStore
內部的 state
再也不經過參數傳入,而是一個局部變量 let state = null
。createStore
的最後會手動調用一次 dispatch({})
,dispatch
內部會調用 stateChanger
,這時候的 state
是 null
,因此此次的 dispatch
其實就是初始化數據了。createStore
內部第一次的 dispatch
致使 state
初始化完成,後續外部的 dispatch
就是修改數據的行爲了。優化
咱們給 stateChanger
這個玩意起一個通用的名字:reducer,不要問爲何,它就是個名字而已,修改 createStore
的參數名字:spa
function createStore (reducer) { let state = null const listeners = [] const subscribe = (listener) => listeners.push(listener) const getState = () => state const dispatch = (action) => { state = reducer(state, action) listeners.forEach((listener) => listener()) } dispatch({}) // 初始化 state return { getState, dispatch, subscribe } }
這是一個最終形態的 createStore
,它接受的參數叫 reducer
,reducer
是一個函數,細心的朋友會發現,它實際上是一個純函數(Pure Function)。code
createStore
接受一個叫 reducer 的函數做爲參數,這個函數規定是一個純函數,它接受兩個參數,一個是 state
,一個是 action
。htm
若是沒有傳入 state
或者 state
是 null
,那麼它就會返回一個初始化的數據。若是有傳入 state
的話,就會根據 action
來「修改「數據,但其實它沒有、也規定不能修改 state
,而是要經過上節所說的把修改路徑的對象都複製一遍,而後產生一個新的對象返回。若是它不能識別你的 action
,它就不會產生新的數據,而是(在 default
內部)把 state
原封不動地返回。對象
reducer 是不容許有反作用的。你不能在裏面操做 DOM,也不能發 Ajax 請求,更不能直接修改 state
,它要作的僅僅是 —— 初始化和計算新的 state
。blog
如今咱們能夠用這個 createStore
來構建不一樣的 store
了,只要給它傳入符合上述的定義的 reducer
便可:
function themeReducer (state, action) { if (!state) return { themeName: 'Red Theme', themeColor: 'red' } switch (action.type) { case 'UPATE_THEME_NAME': return { ...state, themeName: action.themeName } case 'UPATE_THEME_COLOR': return { ...state, themeColor: action.themeColor } default: return state } } const store = createStore(themeReducer) ...