国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

redux 源碼閱讀筆記

魏憲會 / 2489人閱讀

摘要:函數用于為應用的狀態添加監聽,以一個回調函數監聽函數為參數當觸發時執行所有監聽函數,返回能夠取消訂閱的函數。函數用于組合多個中間件返回的函數,其實質是生成新的暴露給用戶。

redux 源碼閱讀筆記 redux 中的 reducer 為什么要叫這個名字?(UPDATED)

筆者在學習 redux 的時候一直感覺 reducer 很不好理解(非要進行翻譯的話,可以稱之為縮減器/折疊器),而像 action 和 store 這兩個概念就比較清晰,直接按照字面意思理解即可,在百思不得其解時發現官方文檔中專門有一句對 reducer 命名的解釋:

It"s called a reducer because it"s the type of function you would pass to Array.prototype.reduce(reducer, ?initialValue).

這句話翻譯過來應該是:之所以被稱為 reducer,是因為該函數與被傳入 Array.prototype.reduce 方法的參數(回調函數)是一種類型的。

我們先來看一個 reduce 方法的例子:

[0, 1, 2, 3, 4].reduce((prev, curr) => prev + curr)
// 10

這與 redux 中的 reducer 模型((prevState, action) => newState)看起來是不是十分相似呢!

reducer 這個術語其實來自于函數式編程,在很多情況下 reduce 操作也被稱為 Fold 折疊,它屬于一種高階函數,可以將其參數(回調函數/reducer)遞歸應用到數組的所有元素上并返回一個獨立的值,這也就是“縮減”或“折疊”的含義所在。

正文如下:

src
├── applyMiddleware.js
├── bindActionCreators.js
├── combineReducers.js
├── compose.js
├── createStore.js
├── index.js
└── utils
    ├── actionTypes.js
    ├── isPlainObject.js
    └── warning.js
index.js
import createStore from "./createStore"
import combineReducers from "./combineReducers"
import bindActionCreators from "./bindActionCreators"
import applyMiddleware from "./applyMiddleware"
import compose from "./compose"
import warning from "./utils/warning"
import __DO_NOT_USE__ActionTypes from "./utils/actionTypes"

/*
 * This is a dummy function to check if the function name has been altered by minification.
 * If the function has been minified and NODE_ENV !== "production", warn the user.
 */
function isCrushed() {}

if (
  process.env.NODE_ENV !== "production" &&
  typeof isCrushed.name === "string" &&
  isCrushed.name !== "isCrushed"
) {
  warning(
    "You are currently using minified code outside of NODE_ENV === "production". " +
      "This means that you are running a slower development build of Redux. " +
      "You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify " +
      "or setting mode to production in webpack (https://webpack.js.org/concepts/mode/) " +
      "to ensure you have the correct code for your production build."
  )
}

export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose,
  __DO_NOT_USE__ActionTypes
}

這里聲明的 isCrushed 函數并不具有實際意義,僅用來判斷在生產環境下 redux 項目是否被壓縮,如果有被壓縮則給開發人員 log 一個警告提示。

createStore.js

createStore 函數是 redux 最核心的 API,它創建一個 store 存放應用的整課 state 樹,并且一個應用中僅能存在一個 store,我們只能通過調用 dispatch 的方法來對 store 中的數據作出更改。

// createStore.js
import $$observable from "symbol-observable"

import ActionTypes from "./utils/actionTypes"
import isPlainObject from "./utils/isPlainObject"

/**
 * Creates a Redux store that holds the state tree.
 * The only way to change the data in the store is to call `dispatch()` on it.
 *
 * There should only be a single store in your app. To specify how different
 * parts of the state tree respond to actions, you may combine several reducers
 * into a single reducer function by using `combineReducers`.
 *
 * @param {Function} reducer A function that returns the next state tree, given
 * the current state tree and the action to handle.
 *
 * @param {any} [preloadedState] The initial state. You may optionally specify it
 * to hydrate the state from the server in universal apps, or to restore a
 * previously serialized user session.
 * If you use `combineReducers` to produce the root reducer function, this must be
 * an object with the same shape as `combineReducers` keys.
 *
 * @param {Function} [enhancer] The store enhancer. You may optionally specify it
 * to enhance the store with third-party capabilities such as middleware,
 * time travel, persistence, etc. The only store enhancer that ships with Redux
 * is `applyMiddleware()`.
 *
 * @returns {Store} A Redux store that lets you read the state, dispatch actions
 * and subscribe to changes.
 */
export default function createStore(reducer, preloadedState, enhancer) {

  // 兼容函數參數不同的情況:應用不存在初始狀態,第二個參數即傳入增強器函數的情況

  if (typeof preloadedState === "function" && typeof enhancer === "undefined") {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== "undefined") {
    if (typeof enhancer !== "function") {
      throw new Error("Expected the enhancer to be a function.")
    }

    return enhancer(createStore)(reducer, preloadedState)
  }

  if (typeof reducer !== "function") {
    throw new Error("Expected the reducer to be a function.")
  }

  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false

  // 確保 nextListeners 和 currentListeners 具有不同的引用

  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  } 

  function getState() {/* ... */}

  function subscribe() {/* ... */}

  function dispatch() {/* ... */}

  function replaceReducer() {/* ... */}

  function observable() {/* ... */}

  // When a store is created, an "INIT" action is dispatched so that every
  // reducer returns their initial state. This effectively populates
  // the initial state tree.
  dispatch({ type: ActionTypes.INIT })

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  }
}

createStore 函數接受三個參數:

reducer:通過 state 和 action 計算 nextState 的函數

preloadedState:初始狀態(需要注意的是如果使用 combineReducers 來生成 reducer,那么必須保證 preloadedState 和 combineReducers 數據結構中的鍵相對應)

enhancer:store 的增強器函數,可以指定為第三方的中間件/時間旅行/持久化等(需要注意的是該函數只能用 redux 提供的 applyMiddleware 函數來生成)

getState
/**
 * Reads the state tree managed by the store.
 *
 * @returns {any} The current state tree of your application.
 */
function getState() {
  if (isDispatching) {
    throw new Error(
      "You may not call store.getState() while the reducer is executing. " +
      "The reducer has already received the state as an argument. " +
      "Pass it down from the top reducer instead of reading it from the store."
    )
  }

  return currentState
}

getState 函數可以用于獲取應用當前的狀態,可以看到這里使用閉包來持久變量,并且所有改變狀態的行為都是改變 currentState 的引用。

subscribe

subscribe 函數用于為應用的狀態添加監聽,以一個回調函數(監聽函數)為參數(當觸發 dispatch 時執行所有監聽函數),返回能夠取消訂閱的 unsubscribe 函數。

/**
 * Adds a change listener. It will be called any time an action is dispatched,
 * and some part of the state tree may potentially have changed. You may then
 * call `getState()` to read the current state tree inside the callback.
 *
 * You may call `dispatch()` from a change listener, with the following
 * caveats:
 *
 * 1. The subscriptions are snapshotted just before every `dispatch()` call.
 * If you subscribe or unsubscribe while the listeners are being invoked, this
 * will not have any effect on the `dispatch()` that is currently in progress.
 * However, the next `dispatch()` call, whether nested or not, will use a more
 * recent snapshot of the subscription list.
 *
 * 2. The listener should not expect to see all state changes, as the state
 * might have been updated multiple times during a nested `dispatch()` before
 * the listener is called. It is, however, guaranteed that all subscribers
 * registered before the `dispatch()` started will be called with the latest
 * state by the time it exits.
 *
 * @param {Function} listener A callback to be invoked on every dispatch.
 * @returns {Function} A function to remove this change listener.
 */
function subscribe(listener) {
  if (typeof listener !== "function") {
    throw new Error("Expected the listener to be a function.")
  }

  if (isDispatching) {
    throw new Error(
      "You may not call store.subscribe() while the reducer is executing. " +
      "If you would like to be notified after the store has been updated, subscribe from a " +
      "component and invoke store.getState() in the callback to access the latest state. " +
      "See https://redux.js.org/api-reference/store#subscribe(listener) for more details."
    )
  }

  let isSubscribed = true

  // 保持監聽序列的不變性

  ensureCanMutateNextListeners()
  nextListeners.push(listener)

  return function unsubscribe() {
    if (!isSubscribed) {
      return
    }

    if (isDispatching) {
      throw new Error(
        "You may not unsubscribe from a store listener while the reducer is executing. " +
        "See https://redux.js.org/api-reference/store#subscribe(listener) for more details."
      )
    }

    isSubscribed = false

    ensureCanMutateNextListeners()
    const index = nextListeners.indexOf(listener)
    nextListeners.splice(index, 1)
  }
}
dispatch

dispatch 函數是改變應用狀態的唯一方法,接受一個 action 作為參數,通過調用 reducer 計算生成一個新的狀態。

/**
 * Dispatches an action. It is the only way to trigger a state change.
 *
 * The `reducer` function, used to create the store, will be called with the
 * current state tree and the given `action`. Its return value will
 * be considered the **next** state of the tree, and the change listeners
 * will be notified.
 *
 * The base implementation only supports plain object actions. If you want to
 * dispatch a Promise, an Observable, a thunk, or something else, you need to
 * wrap your store creating function into the corresponding middleware. For
 * example, see the documentation for the `redux-thunk` package. Even the
 * middleware will eventually dispatch plain object actions using this method.
 *
 * @param {Object} action A plain object representing “what changed”. It is
 * a good idea to keep actions serializable so you can record and replay user
 * sessions, or use the time travelling `redux-devtools`. An action must have
 * a `type` property which may not be `undefined`. It is a good idea to use
 * string constants for action types.
 *
 * @returns {Object} For convenience, the same action object you dispatched.
 *
 * Note that, if you use a custom middleware, it may wrap `dispatch()` to
 * return something else (for example, a Promise you can await).
 */
function dispatch(action) {

  // 基礎版本的實現僅支持 action 為樸素對象的情況

  if (!isPlainObject(action)) {
    throw new Error(
      "Actions must be plain objects. " +
      "Use custom middleware for async actions."
    )
  }

  if (typeof action.type === "undefined") {
    throw new Error(
      "Actions may not have an undefined "type" property. " +
      "Have you misspelled a constant?"
    )
  }

  if (isDispatching) {
    throw new Error("Reducers may not dispatch actions.")
  }

  try {
    isDispatching = true
    currentState = currentReducer(currentState, action)
  } finally {
    isDispatching = false
  }

  // 觸發 dispatch 時執行所有監聽函數

  const listeners = (currentListeners = nextListeners)
  for (let i = 0; i < listeners.length; i++) {
    const listener = listeners[i]
    listener()
  }

  return action
}
replaceReducer
/**
 * Replaces the reducer currently used by the store to calculate the state.
 *
 * You might need this if your app implements code splitting and you want to
 * load some of the reducers dynamically. You might also need this if you
 * implement a hot reloading mechanism for Redux.
 *
 * @param {Function} nextReducer The reducer for the store to use instead.
 * @returns {void}
 */
function replaceReducer(nextReducer) {
  if (typeof nextReducer !== "function") {
    throw new Error("Expected the nextReducer to be a function.")
  }

  currentReducer = nextReducer
  dispatch({
    type: ActionTypes.REPLACE
  })
}
observable

observable 用于提供另外一種觀察者模式的交互方式。

/**
 * Interoperability point for observable/reactive libraries.
 * @returns {observable} A minimal observable of state changes.
 * For more information, see the observable proposal:
 * https://github.com/tc39/proposal-observable
 */
function observable() {
  const outerSubscribe = subscribe
  return {
    /**
     * The minimal observable subscription method.
     * @param {Object} observer Any object that can be used as an observer.
     * The observer object should have a `next` method.
     * @returns {subscription} An object with an `unsubscribe` method that can
     * be used to unsubscribe the observable from the store, and prevent further
     * emission of values from the observable.
     */
    subscribe(observer) {
      if (typeof observer !== "object" || observer === null) {
        throw new TypeError("Expected the observer to be an object.")
      }

      function observeState() {
        if (observer.next) {
          observer.next(getState())
        }
      }

      observeState()
      const unsubscribe = outerSubscribe(observeState)
      return {
        unsubscribe
      }
    },

    [$$observable]() {
      return this
    }
  }
}
compose.js
/**
 * Composes single-argument functions from right to left. The rightmost
 * function can take multiple arguments as it provides the signature for
 * the resulting composite function.
 *
 * @param {...Function} funcs The functions to compose.
 * @returns {Function} A function obtained by composing the argument functions
 * from right to left. For example, compose(f, g, h) is identical to doing
 * (...args) => f(g(h(...args))).
 */

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

compose 函數的設計十分巧妙,從右到左把接受到的函數組合,然后返回一個最終函數,其結果也并不復雜,旨在讓開發人員能夠在不寫深度右括號的情況下來寫深度嵌套的函數。

const fn1 = val => `(fn1: ${val})`
const fn2 = val => `(fn2: ${val})`
const fn3 = val => `(fn3: ${val})`

console.log(compose(fn1,fn2,fn3)("Hello World!"));
// (fn1: (fn2: (fn3: Hello World!)))
applyMiddleware.js

applyMiddleware 函數用于組合多個中間件返回的 enhancer 函數,其實質是生成新的 dispatch 暴露給用戶。

在認識 applyMiddleware 之前,我們先來看看中間件是個什么東西 :)

假設我們現在有這樣的需求:打印出觸發 dispatch 的 action,以及改變后的 state(nextState)。我們很快可以寫出如下代碼,然而這樣做的壞處是我們要在每一個需要 log 的地方加上這些代碼,看起來十分不整潔,即使封裝成一個函數把 dispatch 包裹起來,也需要在很多地方進行 import,這是非常冗余的操作。

console.log("dispatch", action);
store.dispatch(action);
console.log("nextState", store.getState());

考慮到這兒,我們知道可以把 dispatch 方法進行重寫,這樣的話每次在我們調用 dispatch 的時候都可以自動進行 log 操作。

const next = store.dispatch();
store.dispatch = function (action) {
   console.log("dispatch", action);
   store.dispatch(action);
   console.log("nextState", store.getState());
}

進一步封裝就是中間件!

function logger(store) {
  const next = store.dispatch();

  return function(action) {
    console.log("dispatch", action);
    const r = next(action);
    console.log("nextState", store.getState());
    return r;
  };
}

這樣在我們用多個中間件的時候,就可以層層給 dispatch 進行賦值:

middlewares.forEach((middleware) =>
  store.dispatch = middleware(store);
)

在我們上面的做法中,為了讓下一個中間件可以得到最新的 dispatch 函數,在每個中間件中都要給 store.dispatch 賦值,那還有沒有別的做法呢?答案是肯定的,那就是把 next 作為參數傳遞,而不是在中間件中讀取 store.dispatch 獲得。

function logger(store) {
  return function(next) {
    return function(action) {
      console.log("dispatch", action);
      const r = next(action);
      console.log("nextState", store.getState());
      return r;
    };
  };
}

柯里化一下:

const logger = store => next => action => {
  console.log("dispatch", action);
  const r = next(action);
  console.log("nextState", store.getState());
  return r;
};

這樣的話,多個中間件的使用就如下代碼所示:

middlewares.forEach((middleware) => {
  dispatch = middleware(store)(store.dispatch);
});

這樣做的好處是,可以不改變 store.dispatch 本身,僅僅是不把它暴露給用戶而已。

接下來再看 redux 的 applyMiddleware 實現就很清晰:先使用 compose 把多個中間件組合起來,然后再封裝出一個新的 dispatch 返回給用戶。

import compose from "./compose"

/**
 * Creates a store enhancer that applies middleware to the dispatch method
 * of the Redux store. This is handy for a variety of tasks, such as expressing
 * asynchronous actions in a concise manner, or logging every action payload.
 *
 * See `redux-thunk` package as an example of the Redux middleware.
 *
 * Because middleware is potentially asynchronous, this should be the first
 * store enhancer in the composition chain.
 *
 * Note that each middleware will be given the `dispatch` and `getState` functions
 * as named arguments.
 *
 * @param {...Function} middlewares The middleware chain to be applied.
 * @returns {Function} A store enhancer applying the middleware.
 */
export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)
    let dispatch = () => {
      throw new Error(
        `Dispatching while constructing your middleware is not allowed. ` +
          `Other middleware would not be applied to this dispatch.`
      )
    }

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
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)) {
    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("", "")}"`
    )
  }

  const unexpectedKeys = Object.keys(inputState).filter(
    key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]
  )

  unexpectedKeys.forEach(key => {
    unexpectedKeyCache[key] = true
  })

  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 })

    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"
    ) {
      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) {

  // 對象的 key 是對應 value(reducer 函數)的狀態名稱

  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 {
    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]
      // 該狀態對應的 reducer 函數
      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
    }

    return hasChanged ? nextState : state
  }
}

combineReducers 函數接受一組 reducers,組合后返回一個新的 reducer 函數(combine)供 createStore 使用。

bindActionCreators.js

bindActionCreators 用來生成可以直接觸發 action 的函數,并沒什么好說的 = =

function bindActionCreator(actionCreator, dispatch) {
  return function() {
    return dispatch(actionCreator.apply(this, arguments))
  }
}

/**
 * Turns an object whose values are action creators, into an object with the
 * same keys, but with every function wrapped into a `dispatch` call so they
 * may be invoked directly. This is just a convenience method, as you can call
 * `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
 *
 * For convenience, you can also pass a single function as the first argument,
 * and get a function in return.
 *
 * @param {Function|Object} actionCreators An object whose values are action
 * creator functions. One handy way to obtain it is to use ES6 `import * as`
 * syntax. You may also pass a single function.
 *
 * @param {Function} dispatch The `dispatch` function available on your Redux
 * store.
 *
 * @returns {Function|Object} The object mimicking the original object, but with
 * every action creator wrapped into the `dispatch` call. If you passed a
 * function as `actionCreators`, the return value will also be a single
 * function.
 */
export default function bindActionCreators(actionCreators, dispatch) {
  if (typeof actionCreators === "function") {
    return bindActionCreator(actionCreators, dispatch)
  }

  if (typeof actionCreators !== "object" || actionCreators === null) {
    throw new Error(
      `bindActionCreators expected an object or a function, instead received ${
        actionCreators === null ? "null" : typeof actionCreators
      }. ` +
        `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
    )
  }

  const keys = Object.keys(actionCreators)
  const boundActionCreators = {}
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i]
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === "function") {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}
utils actionTypes.js
/**
 * These are private action types ≈ by Redux.
 * For any unknown actions, you must return the current state.
 * If the current state is undefined, you must return the initial state.
 * Do not reference these action types directly in your code.
 */

const randomString = () =>
  Math.random()
    .toString(36)
    .substring(7)
    .split("")
    .join(".")

const ActionTypes = {
  INIT: `@@redux/INIT${randomString()}`,
  REPLACE: `@@redux/REPLACE${randomString()}`,
  PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`
}

export default ActionTypes
isPlainObject.js
/**
 * @param {any} obj The object to inspect.
 * @returns {boolean} True if the argument appears to be a plain object.
 */
export default function isPlainObject(obj) {
  if (typeof obj !== "object" || obj === null) return false

  let proto = obj
  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto)
  }

  return Object.getPrototypeOf(obj) === proto
}

顧名思義 isPlainObject 函數用于判斷 obj 是否為樸素對象(即原型鏈上直接繼承 Object.prototype 的對象,也就是由 {} 或者 new Object() 創建的對象)。

warning.js
/**
 * Prints a warning in the console if it exists.
 *
 * @param {String} message The warning message.
 * @returns {void}
 */
export default function warning(message) {
  /* eslint-disable no-console */
  if (typeof console !== "undefined" && typeof console.error === "function") {
    console.error(message)
  }
  /* eslint-enable no-console */
  try {
    // This error was thrown as a convenience so that if you enable
    // "break on all exceptions" in your console,
    // it would pause the execution at this line.
    throw new Error(message)
  } catch (e) {} // eslint-disable-line no-empty
}

至此結束。

決定代碼質量的是思維,而非框架或語法。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95193.html

相關文章

  • 好程序員React精品項目全集:商城管理后臺(視頻+源碼+筆記

    摘要:今天給大家帶來了好程序員實戰項目商城管理后臺。配合項目學習會讓你更快掌握它的使用方法下面就來看看好程序員這套實戰項目課程介紹好程序員項目本項目是一個使用開發的商城系統的管理后臺,里面登錄判斷,接口調用,數據展示和編輯,文件上傳等后臺功能。 眾所周知,項目經驗對于一個程序員變得越來越重要。在面...

    李世贊 評論0 收藏0
  • FCC 成都社區·技術周刊 第 13 期

    摘要:前端支持同域才能發送本月初,瀏覽器發布。所謂攻擊,就是使用真實的進行惡意行為。鏈接發布此次發布亮點包括類型以及對和映射對象類型中的符號和數字文字的支持。但直到看到了,總算覺得社區又進了一步。微信已將對弈源碼和訓練好的模型開源。 【前端】 1、Firefox 60 支持同域才能發送 Cookie 本月初,Firefox 60 瀏覽器發布。它有一個很大的亮點,就是它解決了 CSRF 攻擊。...

    Seay 評論0 收藏0
  • FCC 成都社區·技術周刊 第 13 期

    摘要:前端支持同域才能發送本月初,瀏覽器發布。所謂攻擊,就是使用真實的進行惡意行為。鏈接發布此次發布亮點包括類型以及對和映射對象類型中的符號和數字文字的支持。但直到看到了,總算覺得社區又進了一步。微信已將對弈源碼和訓練好的模型開源。 【前端】 1、Firefox 60 支持同域才能發送 Cookie 本月初,Firefox 60 瀏覽器發布。它有一個很大的亮點,就是它解決了 CSRF 攻擊。...

    chnmagnus 評論0 收藏0
  • FCC 成都社區·技術周刊 第 13 期

    摘要:前端支持同域才能發送本月初,瀏覽器發布。所謂攻擊,就是使用真實的進行惡意行為。鏈接發布此次發布亮點包括類型以及對和映射對象類型中的符號和數字文字的支持。但直到看到了,總算覺得社區又進了一步。微信已將對弈源碼和訓練好的模型開源。 【前端】 1、Firefox 60 支持同域才能發送 Cookie 本月初,Firefox 60 瀏覽器發布。它有一個很大的亮點,就是它解決了 CSRF 攻擊。...

    wuyangchun 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<