Edit This Page

使用 Object Spread 運算子

因為 Redux 最核心的原則之一就是永不更動 state,你將會常常發現自己使用 Object.assign() 來建立擁有新值或更新其中值的複製 object。例如,在 todoApp 內, Object.assign() 慣於用來回傳一個新的且有更新 visibilityFilter 屬性後的 state object:

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    default:
      return state
  }
}

然而大力使用 Object.assign() 會很快地導致簡單的 reducers 因為其相當冗長的語法而難以閱讀。

一個替代方法就是使用為了下一代 JavaScript 提出的 object spread 語法,這將讓你使用 spread (...) 運算子來更有效地從一個 object 複製 enumerable 屬性到另一個。此 object spread 運算子概念上相似於 ES6 的 array spread operator。我們可以經由使用 object spread 運算子來簡化 todoApp 範例:

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return { ...state, visibilityFilter: action.filter }
    default:
      return state
  }
}

當你正在構成複雜的 objects時,使用 object spread 語法的好處會變的越來越明顯。下面 getAddedIdsgetProductgetQuantity 的回傳值 map 一個 id array 到另一個 object array。

return getAddedIds(state.cart).map(id => Object.assign(
  {},
  getProduct(state.products, id),
  {
    quantity: getQuantity(state.cart, id)
  }
))

Object spread 讓我們簡化以上的 map 呼叫,成為:

return getAddedIds(state.cart).map(id => ({
  ...getProduct(state.products, id),
  quantity: getQuantity(state.cart, id)
}))

因為 object spread 語法仍然是 ECMAScript 的 Stage 2 提案,你將需要使用像是 Babel 的 transpiler 使 production 環境下可以使用它。你可以使用已存在的 es2015 preset,安裝 babel-plugin-transform-object-rest-spread 並逐一將它加入在你 .babelrc 中的 plugins array。

{
  "presets": ["es2015"],
  "plugins": ["transform-object-rest-spread"]
}

注意這仍然個實驗性質的功能提案語法,所以它可能在未來會改變。儘管如此,一些大專案像是 React Native 已經廣泛使用。所以可以安全的說,如果未來它真的改變了也將會有一個好的自動 migration 方式。