你如何使用 `reselect` 来记忆一个数组?
How do you use `reselect` to memoize an array?
假设我有一个具有这种状态结构的 redux 存储:
{
items: {
"id1" : {
foo: "foo1",
bar: "bar1"
},
"id2": {
foo: "foo2",
bar: "bar2"
}
}
}
这家商店通过接收物品的全新价值而发展:
const reduceItems = function(items = {}, action) {
if (action.type === 'RECEIVE_ITEM') {
return {
...items,
[action.payload.id]: action.payload,
};
}
return items;
};
我想显示一个 Root 视图,它呈现一个 SubItem 视图列表,它只提取状态的一部分。
例如 SubItem 视图只关心 foos,应该得到它:
function SubItem({ id, foo }) {
return <div key={id}>{foo}</div>
}
因为我只关心 "subpart" 个状态,这就是我想传递给 "dumb" 根视图的内容:
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>;
};
我可以很容易地连接这个组件来订阅状态的变化:
function mapStatesToProps(state) {
return {
subitems: xxxSelectSubItems(state)
}
}
return connect(mapStatesToProps)(Root)
我的根本问题是当我不关心的状态部分 (bar
) 发生变化时会发生什么。
甚至,当我收到一个项目的新值时,foo
和 bar
都没有改变:
setInterval(() => {
store.dispatch({
type: 'RECEIVE_ITEM',
payload: {
id: 'id1',
foo: 'foo1',
bar: 'bar1',
},
});
}, 1000);
如果我使用 "naive" 选择器实现:
// 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);
});
}
然后列表在每次调用时都是一个全新的对象,我的组件每次都会被渲染,没有任何意义。
当然,如果我使用 'constant' 选择器,那总是 return 相同的列表,因为连接组件是纯的,它被重新渲染(但这只是为了说明连接组件是纯的):
// fully pure implementation
const SUBITEMS = [
{
id: 'id0',
foo: 'foo0',
},
];
function constSelectSubItems(state) {
return SUBITEMS;
}
现在,如果我使用 "almostConst" 版本,其中列表发生变化但包含相同的元素,这会变得有点棘手。
const SUBITEM = {
id: 'id0',
foo: 'foo0',
};
function almostConstSelectSubItems(state) {
return [SUBITEM];
}
现在,可以预见的是,由于列表不同,即使里面的项目相同,组件也会每秒重新渲染一次。
这是我虽然 'reselect' 可以提供帮助的地方,但我想知道我是否没有完全忽略这一点。我可以让 reselect
使用此行为:
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));
});
但它的行为与原始版本完全一样。
所以:
- 尝试记忆一个数组是没有意义的吗?
- 可以重新选择处理吗?
- 我应该改变国家的组织吗?
- 我应该使用 "deepEqual" 测试在 Root 上实现 shouldComponentUpdate 吗?
- 我是否应该放弃 Root 作为连通分量,让每个 LeafItems 本身成为连通分量?
- immutable.js 有帮助吗?
- 它实际上不是问题吗,因为 React 很聪明,一旦计算了 virtual-dom 就不会重新绘制任何东西?
我尝试做的事情可能毫无意义,并且在我的 redux 存储中隐藏了一个问题,所以请随时指出明显的错误。
关于导致重新渲染的新数组引用和 sort 选择器在正确的轨道上,您绝对是正确的,但您确实需要改变一些方法.
而不是让选择器立即 returns Object.keys(state.item)
,您需要处理对象本身:
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 查找自己的数据。看我的博客 post Practical Redux, Part 6: Connected Lists, Forms, and Performance for examples. I also have a bunch of related articles in the Redux Techniques#Selectors and Normalization and Performance#Redux Performance sections of my React/Redux links list.
假设我有一个具有这种状态结构的 redux 存储:
{
items: {
"id1" : {
foo: "foo1",
bar: "bar1"
},
"id2": {
foo: "foo2",
bar: "bar2"
}
}
}
这家商店通过接收物品的全新价值而发展:
const reduceItems = function(items = {}, action) {
if (action.type === 'RECEIVE_ITEM') {
return {
...items,
[action.payload.id]: action.payload,
};
}
return items;
};
我想显示一个 Root 视图,它呈现一个 SubItem 视图列表,它只提取状态的一部分。 例如 SubItem 视图只关心 foos,应该得到它:
function SubItem({ id, foo }) {
return <div key={id}>{foo}</div>
}
因为我只关心 "subpart" 个状态,这就是我想传递给 "dumb" 根视图的内容:
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>;
};
我可以很容易地连接这个组件来订阅状态的变化:
function mapStatesToProps(state) {
return {
subitems: xxxSelectSubItems(state)
}
}
return connect(mapStatesToProps)(Root)
我的根本问题是当我不关心的状态部分 (bar
) 发生变化时会发生什么。
甚至,当我收到一个项目的新值时,foo
和 bar
都没有改变:
setInterval(() => {
store.dispatch({
type: 'RECEIVE_ITEM',
payload: {
id: 'id1',
foo: 'foo1',
bar: 'bar1',
},
});
}, 1000);
如果我使用 "naive" 选择器实现:
// 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);
});
}
然后列表在每次调用时都是一个全新的对象,我的组件每次都会被渲染,没有任何意义。
当然,如果我使用 'constant' 选择器,那总是 return 相同的列表,因为连接组件是纯的,它被重新渲染(但这只是为了说明连接组件是纯的):
// fully pure implementation
const SUBITEMS = [
{
id: 'id0',
foo: 'foo0',
},
];
function constSelectSubItems(state) {
return SUBITEMS;
}
现在,如果我使用 "almostConst" 版本,其中列表发生变化但包含相同的元素,这会变得有点棘手。
const SUBITEM = {
id: 'id0',
foo: 'foo0',
};
function almostConstSelectSubItems(state) {
return [SUBITEM];
}
现在,可以预见的是,由于列表不同,即使里面的项目相同,组件也会每秒重新渲染一次。
这是我虽然 'reselect' 可以提供帮助的地方,但我想知道我是否没有完全忽略这一点。我可以让 reselect
使用此行为:
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));
});
但它的行为与原始版本完全一样。
所以:
- 尝试记忆一个数组是没有意义的吗?
- 可以重新选择处理吗?
- 我应该改变国家的组织吗?
- 我应该使用 "deepEqual" 测试在 Root 上实现 shouldComponentUpdate 吗?
- 我是否应该放弃 Root 作为连通分量,让每个 LeafItems 本身成为连通分量?
- immutable.js 有帮助吗?
- 它实际上不是问题吗,因为 React 很聪明,一旦计算了 virtual-dom 就不会重新绘制任何东西?
我尝试做的事情可能毫无意义,并且在我的 redux 存储中隐藏了一个问题,所以请随时指出明显的错误。
关于导致重新渲染的新数组引用和 sort 选择器在正确的轨道上,您绝对是正确的,但您确实需要改变一些方法.
而不是让选择器立即 returns Object.keys(state.item)
,您需要处理对象本身:
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 查找自己的数据。看我的博客 post Practical Redux, Part 6: Connected Lists, Forms, and Performance for examples. I also have a bunch of related articles in the Redux Techniques#Selectors and Normalization and Performance#Redux Performance sections of my React/Redux links list.