dictionary - 高效的 Redux reducers,避免不必要的对象复制

标签 dictionary functional-programming ecmascript-6 functor redux

我想我的问题也可以概括为类似

Is there an idiomatic ES6 way to have:

array.map(identity) === array ?

array.filter(i => true) === array ?

{obj..., attr: obj.attr} === obj ?

我知道,它还没有像在 ES6 中那样实现,但是是否有一些我可能遗漏的语法或简单的辅助函数来使这些属性为真而不求助于不可变库?


我使用 Babel 和新的 JS 特性,以及不可变的 js 对象。

我想知道如何使我的 reducer 更高效并生成更少不必要的对象副本

我对 lib (Mori/ImmutableJS) 解决方案不感兴趣。

我有一个管理分页列表的 reducer。

pages 属性实际上是一个Array[Array[item]]

这是我的 reducer :

const initialState = {
  isLoading: false,
  pages: [],
  allStamplesLoaded: false
};

function reducer(state = initialState, event) {
  
  switch (event.name) {

    case Names.STAMPLE_DELETED:
      return {
        ...state,
        pages: removeStampleFromPages(state.pages,event.data.stampleId)
      };

    case Names.STAMPLE_UPDATED:
      return {
        ...state,
        pages: updateStampleInPages(state.pages,event.data.apiStample)
      };

    case Names.STAMPLE_PAGES_CLEANED:
      return {
        ...initialState,
      };

    case Names.STAMPLE_PAGE_REQUESTED:
      return {
        ...state,
        isLoading: true
      };

    case Names.STAMPLE_PAGE_LOADED:
      const {stamplePage,isLastPage} = event.data;
      return {
        ...state,
        isLoading: false,
        pages: [...state.pages, stamplePage],
        isLastPage: isLastPage
      };

    case Names.STAMPLE_PAGE_ERROR:
      return {
        ...state,
        isLoading: false
      };

    default:
      return state;
  }
}

我还有这些辅助函数:

function removeStampleFromPages(pages,deletedStampleId) {
  return pages.map(page => {
    return page.filter(apiStample => apiStample != deletedStampleId)
  })
}
function updateStampleInPages(pages,newApiStample) {
  return pages.map(page => {
    return updateStampleInPage(page,newApiStample);
  })
}
function updateStampleInPage(page,newApiStample) {
  return page.map(apiStample => {
    if (apiStample.id === newApiStample.id) {
      return newApiStample;
    }
    else {
      return apiStample;
    }
  })
}

正如您所注意到的,每次触发诸如 STAMPLE_UPDATED 之类的事件时,我的 reducer 总是会返回一个新状态,其中包含一个新的页面数组数组,即使数组实际上已更新。这会产生不必要的对象复制和 GC。

我不想过早地优化它,也不想在我的应用程序中引入不可变库,但我想知道是否有任何惯用的 ES6 方法可以解决这个问题?

最佳答案

Immutable.js 和 Mori 等不可变数据结构使用巧妙的技巧来避免始终重新创建整个结构。

策略相当简单:当您更新一个属性时,向下钻取该属性,更改它并重新包装从该节点到根的所有属性。

假设您想在以下状态下将属性 c 更改为 4:

const state1 = {
  a: {
    b: {
      c: 1
    },
    d: [2, 3, 4],
    e: 'Hello'
  }
}

第一步是将c更新为4。之后你需要创建

  1. b 的新对象(因为 c 改变了)
  2. a 的新对象(因为 b 改变了)
  3. 和状态的新对象(因为 a 改变了)。

您的新状态将如下所示(对象旁边的 * 表示该对象已重新创建)

const state2 = *{
  a: *{
    b: *{
      c: 4
    },
    d: [2, 3, 4],
    e: 'Hello'
  }
}

注意 de 没有被触及。

您现在可以验证一切是否正常工作:

state1 === state2 // false
state1.a === state2.a // false
state1.a.b === state2.a.b //false
state1.d === state2.d // true
state1.e === state2.e // true

您可能会注意到 destate1state2 之间共享。

您可以使用类似的策略在您的状态中共享信息,而无需一直重新创建一个全新的状态。

至于你最初的问题:

array.map(identity) !== array
array.filter(i => true) !== array
{obj..., attr: obj.attr} !== obj

答案很简单。

创建数组或对象时,Javascript VM 会在内部为该对象分配一个标识符。标识符是递增的,因此没有两个数组/对象是相同的。

当您对数组或对象执行身份检查时,只会检查内部标识符是否匹配。

a = [] // internal identifier 1
[] // internal identifier to 2
b = [] // internal identifier 3
a === b // id 1 === id 3 is FALSE!
a === a // id 1 === id 1 is TRUE!

关于dictionary - 高效的 Redux reducers,避免不必要的对象复制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35368616/

相关文章:

algorithm - 简化省份生成

python - 为什么这个 python 字符序列代码给出了意想不到的结果?

random - 初始化榆树应用程序的正确方法是什么

python - 在 python 中对 map 使用递归

javascript - 使用 for..of 迭代时删除 Set 中的元素是否安全?

python - 如何在类中迭代 `dict` 就像只引用 `dict` 一样?

python - 如何以漂亮的方式显示包含词典的列表?

javascript - 使用 Sanctuary.js 合并多个对象

javascript - reduce() 方法从对象数组中返回 NaN

javascript - 使用 webpack 转译 ES6 模块后,模块未导出