動手實現 Redux(五):不要問爲何的 reducer

通過了這麼多節的優化,咱們有了一個很通用的 createStorehtml

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 = nullcreateStore 的最後會手動調用一次 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,它接受的參數叫 reducerreducer 是一個函數,細心的朋友會發現,它實際上是一個純函數(Pure Function)。code

reducer

createStore 接受一個叫 reducer 的函數做爲參數,這個函數規定是一個純函數,它接受兩個參數,一個是 state,一個是 actionhtm

若是沒有傳入 state 或者 state 是 null,那麼它就會返回一個初始化的數據。若是有傳入 state 的話,就會根據 action 來「修改「數據,但其實它沒有、也規定不能修改 state,而是要經過上節所說的把修改路徑的對象都複製一遍,而後產生一個新的對象返回。若是它不能識別你的 action,它就不會產生新的數據,而是(在 default 內部)把 state 原封不動地返回。對象

reducer 是不容許有反作用的。你不能在裏面操做 DOM,也不能發 Ajax 請求,更不能直接修改 state,它要作的僅僅是 —— 初始化和計算新的 stateblog

如今咱們能夠用這個 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)
...

相關文章
相關標籤/搜索