2017-06-01 61 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部分也有一堆相關的文章。