Organizzazione dei riduttori in Redux

0

Userò il seguente esempio popolare di elenco di cose da fare per la dimostrazione. Diciamo che volevo creare un elenco di cose da fare molto semplice con questa struttura come stato dell'app:

{
    todos: {
        todoKey1: {
            content: "todoKey1 content",
            ...
        },
        todoKey2: ...
    }
}

A causa della natura ricorsiva dei riduttori, possiamo separare i riduttori in file diversi:

// reducers/app.js:
import todos from "./todos";
export default function app(prev={}, action){
    return {
        todos: todos(prev.todos, action)
    };
}

// reducers/todos.js:
import todo from "./todo";
export default function todos(prev={}, action){
    switch(action.type){
        case "ADD_TODO":
            return {
                ...prev,
                [action.id]: todo(undefined, action)
            };
        default:
            return prev;
    }
}

// reducers/todo.js:
export default function todo(prev={}, action){
    switch(action.type){
        case "ADD_TODO":
            return {content: action.content};
        default:
            return prev;
    }
}

Tutto sembra buono finora - bello e organizzato. È qui che arriva la mia domanda: non voglio includere la nuova chiave per il nuovo todo nell'azione. Invece, voglio che l'azione sia molto pura, in cui dovrebbe descrivere solo l'azione (aggiungendo un nuovo oggetto da fare). id è irrilevante.

Vorrei che la chiave venisse generata automaticamente ogni volta che aggiungo un oggetto todo e quindi ho modificato la struttura dello stato per includere next_available_id che indica quale dovrebbe essere la chiave nel prossimo articolo da utilizzare:

{
    next_available_id: 0,
    todos: {
        todoKey1: {
            content: "todoKey1 content",
            ...
        },
        todoKey2: ...
    }
}

Questo rompe i simpatici riduttori ricorsivi che ho avuto da quando ora todos() non ha id per creare la nuova voce e l'azione non ha id . Invece, dovrò combinare il riduttore in todos.js in app.js :

// reducers/app.js:
import todo from "./todo";
export default function app(prev={}, action){
    switch(action.type){
        case "ADD_TODO":
            let todos = prev.todos || {};
            return {
                ...prev,
                next_available_id: prev.next_available_id + 1,
                todos: {
                    ...todos,
                    [prev.next_available_id]: todo(undefined, action)
                }
            };
        default:
            return prev;
    }
}

Possibili soluzioni a cui posso pensare:

  • Invio di più azioni: una per l'incremento di next_available_id e un'altra per l'effettiva creazione di un punto.
    • Pro: app.js e todos.js possono rimanere separati
    • Con: Questo può diventare davvero disordinato nell'applicazione reale poiché ci sarebbero molte azioni extra
  • Modifica l'azione stessa prima di passare a todos .
    • Pro: elimina la necessità di un'azione aggiuntiva
    • Con: le azioni devono essere immutabili

Mi sento come se mi mancasse qualcosa qui. Quale sarebbe il modo standard di gestire situazioni come questa?

    
posta Derek 朕會功夫 18.06.2018 - 18:26
fonte

1 risposta

0

Ho trovato una soluzione in cui semplicemente non considero il prossimo ID disponibile come parte dello stato dell'app.

Nel mio actions.js , ho aggiunto un contatore che si incrementa semplicemente ogni volta che viene chiamato addTodo :

let next_available_id = 0;
export function addTodo(content) {
    return {
        id: next_available_id++,
        content
    };
}

Funziona perfettamente e ora sono in grado di separare i riduttori in file diversi ancora una volta. Non sei sicuro di come "corretto" questo è perché questo probabilmente introduce effetti collaterali, ma in questo caso non ha molta importanza.

    
risposta data 19.06.2018 - 07:47
fonte

Leggi altre domande sui tag