https://github.com/reduxjs/redux 版本 4.0.0

 

先了解一下redux是怎么用的,此处摘抄自阮一峰老师的《Redux 入门教程

// Web 应用是一个状态机,视图与状态是一一对应的
// 所有的状态,保存在一个对象里面

// store 是保存数据的地方

// 创建 store
import { createStore } from 'redux'
const store = createStore(fn)

// state 是某一时刻 store 的快照,一个 state 对应一个 view
// 可通过 getState() 获取
const state = store.getState()

// Action 是一个对象 用来表示 view 发出的改变 state 的通知
// type 是必须的 其他属性可以自由设置
const action = {
    type: 'ADD_TODO',
    payload: 'Learn Redux'
}

// 同一种类型的 action 可以写一个函数生成
const ADD_TODO = '添加 TODO'
// 生成 action 的函数: Action Creator
function addTodo(text) {
    return {
        type: ADD_TODO,
        text
    }
}

const action = addTodo('Learn Redux')

// store.dispatch()是 View 发出 Action 的唯一方法。
store.dispatch(action)

// reducer 是 store 接收 state 返回新的 state 的过程

const defaultState = 0
// reducer 接收 action 返回新的 state
const reducer = (state = defaultState, action) => {
    switch(action.type) {
        case: 'ADD':
            return state + action.payload
        default:
            return state
    }
}
const state = reducer(1, {
    type: 'ADD',
    payload: 2
})

// 创建 store 时传入 reducer 当调用 store.dispatch 时将自动调用 reducer
const store = createStore(reducer)

/*
reducer 是一个纯函数,纯函数要求:
- 不得改写参数
- 不能调用系统 I/O 的API
- 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果
*/

// store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数
// 返回解除监听函数
let unsubscribe = store.subsribe(() => { console.log(store.getState) })
unsubscribe() // 解除监听

/*
store 提供的三个方法
- store.getState()
- store.dispatch()
- store.subscribe()
*/

// createStore方法还可以接受第二个参数,表示 State 的最初状态。这通常是服务器给出的。
// !这个初始值会覆盖 Reducer 函数默认的初始值
let store = createStore(todoApp,  STATE_FROM_SERVER)

// createStore 的简单实现
const createStore = (reducer) => {
    let state
    let listeners = []

    const getState = () => state

    const dispatch = action => {
        state = reducer(state, action)
        listeners.forEach(listener => listener())
    }

    const subscribe = listener => {
        listeners.push(listener)
        return () => {
            listeners = listeners.filter(l => l !== listener)
        }
    }

    dispatch({})

    return { getState, dispatch, subscribe }

}

// 可以通过 combineReducers 来将多个 Reducer 合为一个
import { combineReducers } from 'redux'

const chatReducer = combineReducers({
    chatLog,
    statusMessage,
    userName
})

// combineReducer 的简单实现
const combineReducers = reducers => {
    return (state = {}, action) => 
        Object.keys(reducers).reduce(
            (nextState, key) => {
                nextState[key] = reducers[key](state[key], action)
                return nextState
            },
            {}
        )
}

 

工作流程

Redux Flow

        dispatch(action)   (previousState, action)
Action Creators ======> Store ======> Reducers
   ^                     ||   <======
    \_                   ||   (newState)
      \_         (state) ||
        \_               ||
(view opt)\_             \/
            \---   React Comonents

 

 

OK 可以开始看源码了~ 网上Redux源码分析的博客真的非常多.. 不过当你知道他的源码究竟有多短 就能理解了hhh 

combineReducers.js 

代码一共179行 多是错误处理 我先将错误处理全部删掉 便只剩28行.....

思路就是创建一个对象 将 Reducer 全部放进去

当Action传进来的时候 就让每一个Reducer去处理这个action

每个Reducer都有一个对应的key 只处理state中对应字段 state[key] 没有Reducer对应的字段会被忽略

截取出核心代码 + 用法、感觉并不需要注释、逻辑都很直接

function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]

    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)


  return function combination(state = {}, action) {
    let hasChanged = false
    const nextState = {}
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      const previousStateForKey = state[key]
      const nextStateForKey = reducer(previousStateForKey, action)

      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    // 如果state每一个key都没有被修改 就直接返回原state
    return hasChanged ? nextState : state
  }
}

/***************** 下面是简单的用法实例 *****************/
function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return state.concat(action.text)
    default:
      return state
  }
}

function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}

let reducer = combineReducers({ list: todos, number: counter })
let state = { list: [], number: 0, otherKey: 'no reducer match will be ignore' }
console.log(state) // { list: [], number: 0, otherKey: 'no reducer match will be ignore' }
state = reducer(state, { type: 'ADD_TODO', text: 'study' })
console.log(state) // { list: [ 'study' ], number: 0 }
state = reducer(state, { type: 'ADD_TODO', text: 'sleep' })
console.log(state) // { list: [ 'study', 'sleep' ], number: 0 }
state = reducer(state, { type: 'INCREMENT' })
console.log(state) // { list: [ 'study', 'sleep' ], number: 1 }

 

combineReducers.js 源码

import ActionTypes from './utils/actionTypes'
import warning from './utils/warning'
import isPlainObject from './utils/isPlainObject'

function getUndefinedStateErrorMessage(key, action) {
  const actionType = action && action.type
  const actionDescription =
    (actionType && `action "${String(actionType)}"`) || 'an action'

  return (
    `Given ${actionDescription}, reducer "${key}" returned undefined. ` +
    `To ignore an action, you must explicitly return the previous state. ` +
    `If you want this reducer to hold no value, you can return null instead of undefined.`
  )
}

function getUnexpectedStateShapeWarningMessage(
  inputState,
  reducers,
  action,
  unexpectedKeyCache
) {
  const reducerKeys = Object.keys(reducers)
  const argumentName =
    action && action.type === ActionTypes.INIT
      ? 'preloadedState argument passed to createStore'
      : 'previous state received by the reducer'

  if (reducerKeys.length === 0) {
    return (
      'Store does not have a valid reducer. Make sure the argument passed ' +
      'to combineReducers is an object whose values are reducers.'
    )
  }

  if (!isPlainObject(inputState)) {
    // 希望 inputState 是一个简单对象:通过 new Object() 、 {} 创建 (Object.create(null) 这里好像是不合法的
    // [object Array] 中提取 'Array'
    // Object.prototype.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1]
    return (
      `The ${argumentName} has unexpected type of "` +
      {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
      `". Expected argument to be an object with the following ` +
      `keys: "${reducerKeys.join('", "')}"`
    )
  }
  // 检查所有Reducer都没有处理到的key ( 此处实在不解 unexpectedKeyCache 到底何用= =
  const unexpectedKeys = Object.keys(inputState).filter(
    key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]
  )

  unexpectedKeys.forEach(key => {
    unexpectedKeyCache[key] = true
  })
  // 替换 store 的 Reducer 时会调用 dispatch({ type: ActionTypes.REPLACE })
  if (action && action.type === ActionTypes.REPLACE) return

  if (unexpectedKeys.length > 0) {
    return (
      `Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
      `"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` +
      `Expected to find one of the known reducer keys instead: ` +
      `"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`
    )
  }
}

function assertReducerShape(reducers) {
  Object.keys(reducers).forEach(key => {
    const reducer = reducers[key]
    const initialState = reducer(undefined, { type: ActionTypes.INIT })
      // Reducer"$ {key}"在初始化时返回undefined。如果传递给reducer的状态未定义,你必须明确返回初始状态。
      // 初始状态可以是不可定义。如果你不想为这个reducer设置一个值,你可以使用null而不是undefined。
    if (typeof initialState === 'undefined') {
      throw new Error(
        `Reducer "${key}" returned undefined during initialization. ` +
          `If the state passed to the reducer is undefined, you must ` +
          `explicitly return the initial state. The initial state may ` +
          `not be undefined. If you don't want to set a value for this reducer, ` +
          `you can use null instead of undefined.`
      )
    }

    if (
      typeof reducer(undefined, {
        type: ActionTypes.PROBE_UNKNOWN_ACTION()
      }) === 'undefined'
    ) {
      // 当使用随机类型探测Reducer${key}时返回undefined。
      // 不要试图处理${ActionTypes.INIT}或者其他在"redux/*"命名空间的动作。它们被认为是私有的。
      // 相反,当你遇到任何未知动作时,你必须返回当前的state,除非当前state是undefined,
      // 那样你要返回初始状态,而不管动作类型。初始状态不可以是undefined,但可以为null
      throw new Error(
        `Reducer "${key}" returned undefined when probed with a random type. ` +
          `Don't try to handle ${
            ActionTypes.INIT
          } or other actions in "redux/*" ` +
          `namespace. They are considered private. Instead, you must return the ` +
          `current state for any unknown actions, unless it is undefined, ` +
          `in which case you must return the initial state, regardless of the ` +
          `action type. The initial state may not be undefined, but can be null.`
      )
    }
  })
}

/**
 * Turns an object whose values are different reducer functions, into a single
 * reducer function. It will call every child reducer, and gather their results
 * into a single state object, whose keys correspond to the keys of the passed
 * reducer functions.
 *
 * @param {Object} reducers An object whose values correspond to different
 * reducer functions that need to be combined into one. One handy way to obtain
 * it is to use ES6 `import * as reducers` syntax. The reducers may never return
 * undefined for any action. Instead, they should return their initial state
 * if the state passed to them was undefined, and the current state for any
 * unrecognized action.
 *
 * @returns {Function} A reducer function that invokes every reducer inside the
 * passed object, and builds a state object with the same shape.
 */
export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]

    if (process.env.NODE_ENV !== 'production') {
      if (typeof reducers[key] === 'undefined') {
        warning(`No reducer provided for key "${key}"`)
      }
    }

    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)

  let unexpectedKeyCache
  if (process.env.NODE_ENV !== 'production') {
    unexpectedKeyCache = {}
  }

  let shapeAssertionError
  try {
    // 判断每个reducer都有初始值和对于未知action返回原state
    assertReducerShape(finalReducers)
  } catch (e) {
    shapeAssertionError = e
  }

  return function combination(state = {}, action) {
    if (shapeAssertionError) {
      throw shapeAssertionError
    }

    if (process.env.NODE_ENV !== 'production') {
      const warningMessage = getUnexpectedStateShapeWarningMessage(
        state,
        finalReducers,
        action,
        unexpectedKeyCache
      )
      if (warningMessage) {
        warning(warningMessage)
      }
    }

    let hasChanged = false
    const nextState = {}
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      const previousStateForKey = state[key]
      const nextStateForKey = reducer(previousStateForKey, action)
      if (typeof nextStateForKey === 'undefined') {
        const errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    // 如果state每一个key都没有被修改 就直接返回原state
    return hasChanged ? nextState : state
  }
}
View Code

相关文章: