经过了这么多节的优化,我们有了一个很通用的 createStore

  1. function createStore (state, stateChanger) {
  2. const listeners = []
  3. const subscribe = (listener) => listeners.push(listener)
  4. const getState = () => state
  5. const dispatch = (action) => {
  6. state = stateChanger(state, action) // 覆盖原对象
  7. listeners.forEach((listener) => listener())
  8. }
  9. return { getState, dispatch, subscribe }
  10. }

它的使用方式是:

  1. let appState = {
  2. title: {
  3. text: 'React.js 小书',
  4. color: 'red',
  5. },
  6. content: {
  7. text: 'React.js 小书内容',
  8. color: 'blue'
  9. }
  10. }
  11. function stateChanger (state, action) {
  12. switch (action.type) {
  13. case 'UPDATE_TITLE_TEXT':
  14. return {
  15. ...state,
  16. title: {
  17. ...state.title,
  18. text: action.text
  19. }
  20. }
  21. case 'UPDATE_TITLE_COLOR':
  22. return {
  23. ...state,
  24. title: {
  25. ...state.title,
  26. color: action.color
  27. }
  28. }
  29. default:
  30. return state
  31. }
  32. }
  33. const store = createStore(appState, stateChanger)
  34. ...

我们再优化一下,其实 appStatestateChanger 可以合并到一起去:

  1. function stateChanger (state, action) {
  2. if (!state) {
  3. return {
  4. title: {
  5. text: 'React.js 小书',
  6. color: 'red',
  7. },
  8. content: {
  9. text: 'React.js 小书内容',
  10. color: 'blue'
  11. }
  12. }
  13. }
  14. switch (action.type) {
  15. case 'UPDATE_TITLE_TEXT':
  16. return {
  17. ...state,
  18. title: {
  19. ...state.title,
  20. text: action.text
  21. }
  22. }
  23. case 'UPDATE_TITLE_COLOR':
  24. return {
  25. ...state,
  26. title: {
  27. ...state.title,
  28. color: action.color
  29. }
  30. }
  31. default:
  32. return state
  33. }
  34. }

stateChanger 现在既充当了获取初始化数据的功能,也充当了生成更新数据的功能。如果有传入 state 就生成更新数据,否则就是初始化数据。这样我们可以优化 createStore 成一个参数,因为 statestateChanger 合并到一起了:

  1. function createStore (stateChanger) {
  2. let state = null
  3. const listeners = []
  4. const subscribe = (listener) => listeners.push(listener)
  5. const getState = () => state
  6. const dispatch = (action) => {
  7. state = stateChanger(state, action)
  8. listeners.forEach((listener) => listener())
  9. }
  10. dispatch({}) // 初始化 state
  11. return { getState, dispatch, subscribe }
  12. }

createStore 内部的 state 不再通过参数传入,而是一个局部变量 let state = nullcreateStore 的最后会手动调用一次 dispatch({})dispatch 内部会调用 stateChanger,这时候的 statenull,所以这次的 dispatch 其实就是初始化数据了。createStore 内部第一次的 dispatch 导致 state 初始化完成,后续外部的 dispatch 就是修改数据的行为了。

我们给 stateChanger 这个玩意起一个通用的名字:reducer,不要问为什么,它就是个名字而已,修改 createStore 的参数名字:

  1. function createStore (reducer) {
  2. let state = null
  3. const listeners = []
  4. const subscribe = (listener) => listeners.push(listener)
  5. const getState = () => state
  6. const dispatch = (action) => {
  7. state = reducer(state, action)
  8. listeners.forEach((listener) => listener())
  9. }
  10. dispatch({}) // 初始化 state
  11. return { getState, dispatch, subscribe }
  12. }

这是一个最终形态的 createStore,它接受的参数叫 reducerreducer 是一个函数,细心的朋友会发现,它其实是一个纯函数(Pure Function)。

reducer

createStore 接受一个叫 reducer 的函数作为参数,这个函数规定是一个纯函数,它接受两个参数,一个是 state,一个是 action

如果没有传入 state 或者 statenull,那么它就会返回一个初始化的数据。如果有传入 state 的话,就会根据 action 来“修改“数据,但其实它没有、也规定不能修改 state,而是要通过上节所说的把修改路径的对象都复制一遍,然后产生一个新的对象返回。如果它不能识别你的 action,它就不会产生新的数据,而是(在 default 内部)把 state 原封不动地返回。

reducer 是不允许有副作用的。你不能在里面操作 DOM,也不能发 Ajax 请求,更不能直接修改 state,它要做的仅仅是 —— 初始化和计算新的 state

现在我们可以用这个 createStore 来构建不同的 store 了,只要给它传入符合上述的定义的 reducer 即可:

  1. function themeReducer (state, action) {
  2. if (!state) return {
  3. themeName: 'Red Theme',
  4. themeColor: 'red'
  5. }
  6. switch (action.type) {
  7. case 'UPATE_THEME_NAME':
  8. return { ...state, themeName: action.themeName }
  9. case 'UPATE_THEME_COLOR':
  10. return { ...state, themeColor: action.themeColor }
  11. default:
  12. return state
  13. }
  14. }
  15. const store = createStore(themeReducer)
  16. ...

因为第三方评论工具有问题,对本章节有任何疑问的朋友可以移步到 React.js 小书的论坛 发帖,我会回答大家的疑问。