首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用“`reselect`”来回溯数组?

如何使用“`reselect`”来回溯数组?
EN

Stack Overflow用户
提问于 2017-06-01 10:54:16
回答 1查看 10.6K关注 0票数 18

假设我有一个具有以下状态结构的redux存储:

代码语言:javascript
复制
{
  items: {
    "id1" : {
      foo: "foo1",
      bar: "bar1"
    },
    "id2": {
      foo: "foo2",
      bar: "bar2"
    } 
  }
}

这个商店是通过接收全新的物品价值来进化的:

代码语言:javascript
复制
const reduceItems = function(items = {}, action) {
  if (action.type === 'RECEIVE_ITEM') {
    return {
      ...items,
      [action.payload.id]: action.payload,
    };
  }
  return items;
};

我想要显示一个根视图,它呈现SubItem视图的列表,该列表只提取状态的一部分。例如,SubItem视图只关心foos,应该得到它:

代码语言:javascript
复制
function SubItem({ id, foo }) {
  return <div key={id}>{foo}</div>
}

因为我只关心状态的“子部分”,所以我想传递给“哑”根视图:

代码语言:javascript
复制
const Root = function({ subitems }) {
  // subitems[0] => { id: 'id1', foo: "foo1" }
  // subitems[1] => { id; 'id2', foo : "foo2" }
  const children = subitems.map(SubItem);
  return <div>{children}</div>;
};

我可以轻松地连接这个组件来订阅状态中的更改:

代码语言:javascript
复制
function mapStatesToProps(state) {    
 return {
    subitems: xxxSelectSubItems(state)
 }
}
return connect(mapStatesToProps)(Root)

我的基本问题是,当我不关心的状态(bar)发生变化时,会发生什么。甚至,当我收到一个项的新值时,foobar都没有改变:

代码语言:javascript
复制
setInterval(() => {
    store.dispatch({
      type: 'RECEIVE_ITEM',
      payload: {
        id: 'id1',
        foo: 'foo1',
        bar: 'bar1',
      },
    });
  }, 1000);

如果我使用“朴素”选择器实现:

代码语言:javascript
复制
// naive version
function toSubItem(id, item) {
  const foo = item.foo;
  return { id, foo };
}

function dumbSelectSubItems(state) {
  const ids = Object.keys(state.items);
  return ids.map(id => {
    const item = state.items[id];
    return toSubItem(id, item);
  });
}

然后,列表在每次调用时都是一个全新的对象,而我的组件每次都会被渲染,没有任何意义。

当然,如果我使用“常量”选择器,它总是返回相同的列表,因为连接的组件是纯的,所以它是重新呈现的(但这只是为了说明连接的组件是纯的):

代码语言:javascript
复制
// fully pure implementation
const SUBITEMS = [
  {
    id: 'id0',
    foo: 'foo0',
  },
];
function constSelectSubItems(state) {
  return SUBITEMS;
}

现在,如果我使用"almostConst“版本更改列表,但包含相同的元素,这就有点棘手了。

代码语言:javascript
复制
const SUBITEM = {
  id: 'id0',
  foo: 'foo0',
};
function almostConstSelectSubItems(state) {
  return [SUBITEM];
}

现在,可以预见的是,由于列表是不同的,即使内部的项是相同的,组件每秒钟都会被重命名。

这就是我认为“重选”可能会有所帮助的地方,但我想知道我是否完全忽略了这一点。我可以让reselect使用以下方法进行操作:

代码语言:javascript
复制
const reselectSelectIds = (state, props) => Object.keys(state.items);
const reselectSelectItems = (state, props) => state.items;
const reselectSelectSubItems = createSelector([reSelectIds, reSelectItems], (ids, items) => {
  return ids.map(id => toSubItem(id, items));
});

但是,它的行为完全像天真的版本。

所以:

  • 试着回忆数组是不是毫无意义?
  • 能重新选择处理这个吗?
  • 我应该改变国家的组织吗?
  • 我应该使用“shouldComponentUpdate”测试在根上实现deepEqual吗?
  • 我是否应该放弃根是一个连接组件,并使每个LeafItems本身都是连接组件?
  • immutable.js能帮上忙吗?
  • 它实际上不是一个问题,因为React是明智的,并且在计算虚拟域之后不会重新绘制任何内容吗?

这是可能的,我试图做他的毫无意义的,并隐藏在我的redux商店的问题,所以请随意声明明显的错误。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-06-01 17:17:15

对于导致重新呈现的新数组引用,您肯定是正确的,并且选择器在某种程度上是正确的,但是您确实需要更改一些方法。

与其拥有立即返回Object.keys(state.item)的选择器,还需要处理对象本身:

代码语言:javascript
复制
const selectItems = state => state.items;

const selectSubItems = createSelector(
    selectItems,
    (items) => {
        const ids = Object.keys(items);
        return ids.map(id => toSubItem(id, items));
    }
);

这样,只有在替换state.items对象时才会重新计算数组。

除此之外,是的,您可能还想要连接您的单个列表项组件,以便每个组件都可以通过ID查找自己的数据。我还在我的Redux Techniques#Selectors与规范化Performance#Redux性能部分的Redux/Redux链接列表中有很多相关的文章。

票数 22
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44305631

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档