2017-06-01 60 views
2

假设我有这个状态结构中的终极版店:如何使用`reselect`来记忆数组?

{ 
    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; 
}; 

我想显示呈现列表的根视图SubItem视图,只提取一部分状态。 例如子项目视图只关心FOOS,并且应该得到它:

function SubItem({ id, foo }) { 
    return <div key={id}>{foo}</div> 
} 

因为我只关心美国的“分部”,这就是我想通过一个“哑”根观点是什么:

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) 

我的根本问题是,当发生什么I D状态的一部分不关心(bar)的变化。 甚至,当我收到一个项目,那里既没有foo也不bar已经改变的新值:

setInterval(() => { 
    store.dispatch({ 
     type: 'RECEIVE_ITEM', 
     payload: { 
     id: 'id1', 
     foo: 'foo1', 
     bar: 'bar1', 
     }, 
    }); 
    }, 1000); 

如果我用“天真”选择执行:

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

然后列表每个被调用的对象都是一个全新的对象,而且我的组件每次都会呈现,无所事事。

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

// 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以此来表现:

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

但后来它的行为完全一样的幼稚版本。

所以:

  • 是毫无意义,试图memoize的阵列?
  • 可以重新选择处理这个吗?
  • 我应该改变国家的组织吗?
  • 我应该只使用“deepEqual”测试在Root上实现shouldComponentUpdate?
  • 我应该放弃Root作为一个连接组件,并且让每个LeafItems都是连接组件吗?
  • 可以immutable.js帮忙吗?
  • 是不是不是问题,因为React很聪明,一旦虚拟dom被计算出来就不会重新绘制任何东西。

这是可能的,我试图做他毫无意义,并在我的REDEX存储隐藏一个问题,所以随时陈述明显的错误。

回答

2

对于引用重新渲染的新数组引用,以及排序在正确的轨道上与您的选择器,您肯定是对的,但您确实需要改变您的方法。

而不是有选择立即返回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查找自己的数据。有关示例,请参阅我的博客文章Practical Redux, Part 6: Connected Lists, Forms, and Performance。我的React/Redux links listRedux Techniques#Selectors and NormalizationPerformance#Redux Performance部分也有一堆相关的文章。