2017-12-21 2 views
4

Ich lese die Dokumentation für Redux und stecken geblieben mit reselect. Der folgende Code erstellt einen Selektor und die Dokumentation sagt, wenn wir es in zwei VisibleTodoList Komponenten verwenden möchten, dann funktioniert es nicht korrekt.Erneut korrekt memoisieren mit mehreren Instanzen der gleichen Komponente

import { createSelector } from 'reselect' 

const getVisibilityFilter = (state, props) => state.todoLists[props.listId].visibilityFilter 

const getTodos = (state, props) => state.todoLists[props.listId].todos 

const getVisibleTodos = createSelector([getVisibilityFilter, getTodos], (visibilityFilter, todos) => { 
    switch (visibilityFilter) { 
    case 'SHOW_COMPLETED': 
     return todos.filter(todo => todo.completed) 
    case 'SHOW_ACTIVE': 
     return todos.filter(todo => !todo.completed) 
    default: 
     return todos 
    } 
}) 

export default getVisibleTodos 

Mit dem getVisibleTodos Wähler mit mehreren Instanzen des visibleTodoList Behälter nicht richtig memoize

const mapStateToProps = (state, props) => { 
    return { 
    // WARNING: THE FOLLOWING SELECTOR DOES NOT CORRECTLY MEMOIZE 
    todos: getVisibleTodos(state, props) 
    } 
} 

Was bedeutet das? Ich kann nicht herausfinden, warum es nicht funktionieren würde.

Antwort

13

Korrekt. Das ist, weil Reselect standardmäßig nur memoizes auf dem neuesten Satz der Eingänge:

const a = someSelector(state, 1); // first call, not memoized 
const b = someSelector(state, 1); // same inputs, memoized 
const c = someSelector(state, 2); // different inputs, not memoized 
const d = someSelector(state, 1); // different inputs from last time, not memoized 

In diesen Fällen die Wähler noch Daten abruft, es muss nur das Ergebnis neu zu berechnen, auch wenn es die Eingänge an einem gewissen Punkt in der Säge Vergangenheit.

Also, wenn Sie einen Selektor in einer mapState Funktion verwenden, und es verweist auf einen Wert von ownProps, dann mehrere Instanzen der Komponente wird wahrscheinlich dazu führen, die Wähler nie

const mapState = (state, ownProps) => { 
    const item = selectItemForThisComponent(state, ownProps.itemId); 

    return {item}; 
} 


// later 
<SomeComponent itemId={1} /> 
<SomeComponent itemId={2} /> 

In diesem Beispiel richtig memoize , selectItemForThisComponent wird immer mit (state, 1) und (state, 2) Rücken an Rücken aufgerufen, so dass es nicht rechts memoize.

Eine Lösung besteht darin, die von connect unterstützte "Factory Function" -Syntax zu verwenden. Wenn Ihre mapState-Funktion beim ersten Aufruf eine Funktion zurückgibt, wird connect diese als echte mapState Implementierung verwenden. Auf diese Weise können Sie einzigartige Selektoren pro Komponenteninstanz erstellen:

const makeUniqueSelectorInstance =() => createSelector(
    [selectItems, selectItemId], 
    (items, itemId) => items[itemId] 
);  


const makeMapState = (state) => { 
    const selectItemForThisComponent = makeUniqueSelectorInstance(); 

    return function realMapState(state, ownProps) { 
     const item = selectItemForThisComponent(state, ownProps.itemId); 

     return {item}; 
    } 
} 

export default connect(makeMapState)(SomeComponent); 

Sowohl die Komponente 1 und Komponente 2 ihre eigenen einzigartigen Kopien von selectItemForThisComponent bekommen, und jede Kopie wird mit konsequent wiederholbaren Eingängen aufgerufen, so dass die richtige memoization.

Update

Ich habe in meinem Blogbeitrag Idiomatic Redux: Using Reselect Selectors for Performance and Encapsulation zu dieser Antwort erweitert.

+0

danke für die Antwort, es ist wirklich interessant. –

Verwandte Themen