Ich habe einen Reduzierungsbaum, der so aussieht:

module.exports = combineReducers({
    routing: routeReducer,
    app: combineReducers({
        setup: combineReducers({
            sets,
            boosters
        }),
        servers: combineReducers({
            servers
        })
    })
});

Jetzt enthält der Schlüssel setup ein Formular, das zurückgesetzt werden muss, sobald wir es gesendet haben. Ich habe jedoch keine Möglichkeit, auf den gesamten setup -Baum zuzugreifen, da bei Verwendung von combinReducers die Reduzierer nur die Daten an den Blattknoten des Baums bearbeiten (in diesem Fall sets und boosters).

Mein erster Impuls ist, eine Funktion zu erstellen, die den gesamten Setup-Baum wie folgt reduziert:

function setup(state, action){
    //If there's an action that affects this whole tree, handle it
    switch(action.type){
        case "FORM_SUBMIT": //DO STUFF
        break;
    }

    //Otherwise just let the reducers care about their own data
    return combineReducers({
                sets,
                boosters
    })(state);
}

Aber das funktioniert nicht und bringt auch die schöne Baumstruktur meines ersten Codebeispiels durcheinander.

Gibt es dafür eine bessere Lösung mit Redux?

5
Migwell 23 Dez. 2015 im 05:46

2 Antworten

Beste Antwort

combineReducers ist ein schönes Muster, da es die Idee erzwingt, dass die Reduzierungen auf nicht überlappende Teilmengen des Geschäfts beschränkt sein sollten, die von der Struktur des Geschäfts selbst entkoppelt sind. Es ist der Meinung, dass Sie die Blätter reduzieren sollten, nicht die Zweige, und es kümmert sich um die Reduzierung der Zweige.

Es kann jedoch gute Gründe geben, ein alternatives Muster zu verwenden. Wie ich in einer leicht verwandten Frage erwähnt habe, Sie können die reine Verwendung von combineReducers deaktivieren und Ihre Reduzierungen nach Belieben zerlegen.

In Ihrem Fall könnten Sie Ihr inneres combineReducers dekorieren:

module.exports = combineReducers({
    routing: routeReducer,
    app: combineReducers({
        setup: setupReducer(
            combineReducers({
                sets,
                boosters
            })
        ),
        servers: combineReducers({
            servers
        })
    })
});

Hier ist setupReducer eine Funktion höherer Ordnung. Es kann schwierig sein, darüber nachzudenken, aber so gehe ich vor:

  • Wir wissen, dass setupReducer einen Reduzierer als Argument verwendet, weil wir das Ergebnis von combineReducers an ihn übergeben.
  • Wir wissen, dass die von combineReducers zurückgegebene Signatur des Reduzierers (state, action) => state lautet.
  • Wir wissen auch, dass setupReducer einen Reduzierer zurückgeben muss, der wiederum eine Funktion der Signatur (state, action) => state ist.

Mit anderen Worten, es wird ein Reduzierer benötigt und ein Reduzierer zurückgegeben: ((state, action) => state) => ((state, action) => state). So könnte es aussehen:

function setupReducer(subReducer) {
    return (state, action) => {
        //If there's an action that affects this whole tree, handle it
        switch(action.type){
            case "FORM_SUBMIT": 
                // ... create newState
                return newState;
            default:
                return subReducer(state, action);
        }
    }
}

Ich habe Ihren Logikfluss oben beibehalten, aber zur Vorsicht möchten Sie möglicherweise subReducer bedingungslos aufrufen und dann die Ausgabe ändern. Andernfalls müssen Sie sicherstellen, dass Ihre Zweige, in denen sie nicht aufgerufen werden, immer ein Objekt mit derselben Form erzeugen, was wie ein potenzieller klebriger Kopplungspunkt erscheint.

9
Community 23 Mai 2017 im 12:00

@ acjays Antwort ist eine großartige Idee! Ich wollte nur die alte Methode reducer(state, action) verwenden und keine Funktion höherer Ordnung. Also habe ich eine Methode erstellt, die den Reduzierer in einer Master-Slave-Beziehung zusammensetzt.

MasterSlave

export default function masterSlaveReducer(reducerMaster, reducerSlave) {
    return (state, action) => {
        const newState = reducerMaster(state, action);
        if(newState === state){
            return reducerSlave(state, action);
        }

        return newState;
    };
}

Miguels Code würde dann folgendermaßen verwendet:

module.exports = combineReducers({
    routing: routeReducer,
    app: combineReducers({
        setup: masterSlaveReducer(
            setup, // setup() from Miguel's question
            combineReducers({
                sets,
                boosters
            })
        ),
        servers: combineReducers({
            servers
        })
    })
});
0
Amio.io 28 Juli 2016 im 11:50