2 votes

Ngrx: Réducteurs génériques

J'ai une application qui comporte plusieurs magasins avec la même fonctionnalité de réducteur. J'ai essayé de créer un réducteur générique et cela a bien fonctionné.

paramètres du réducteur générique :

interface State {
    itemList : T[]
}

const initialState: State = {
    itemList: []
}

const createReducer = (type: string) => {
    return (state = initialState, action: any): State => {
        switch (action.type) {
            case types.get(type).Add:
                return {
                    ...state,
                    itemList: [...state.itemList, action.payload]
                }

            case types.get(type).AddList:
                return {
                    ...state,
                    itemList: [...state.itemList, ...action.payload]
                };

            default:
                return state;
        }
    }
}

Ensuite, je combinerais les réducteurs comme suit :

export const reducers: ActionReducerMap = {
    vehiculeState: createReducer('vehicule'),
    rentState: createReducer('rent'),
    clientState : createReducer('client'),
    companionState : createReducer('companion'),
    paymentState : createReducer('payment'),
    notificationState : createReducer('notification'),
    employeeState : createReducer('employee')
}

Le problème est que en passant en mode générique, je devrais réécrire une grande partie de mon application, car j'ai déjà créé plusieurs réducteurs avec des états contenant des propriétés nommées (clientList, vehiculeList, ...), et le nom de la propriété itemList n'est pas très informatif. Ma question est donc : comment passer en mode générique tout en conservant les propriétés de l'état telles quelles?

Un exemple de réducteurs actuels :

export function rentReducer(state = initialState, action: rentActions.RentActions): State {
    switch(action.type){
        case rentActions.ADD_RENT:
        return{
            ...state,
            rentList : [...state.rentList, action.payload]
        }

        case rentActions.ADD_RENT_LIST:
        return {
            ...state,
            rentList : [...state.rentList, ...action.payload]
        };

        default:
        return state;
    }

}

5voto

devilmaster Points 439

Vous pourriez utiliser une chaîne de caractères pour représenter le nom de la propriété itemList, et utiliser des types cartographiés pour transformer cette chaîne en une propriété sûre pour le type State. Un inconvénient est que l'utilisation de l'opérateur de propagation n'est pas prise en charge pour les types cartographiés, mais nous pouvons obtenir un effet similaire avec Object.assign

// Nous ajoutons un paramètre supplémentaire à State qui sera le nom de la propriété passé en tant que type littéral de chaîne de caractères
// Donc State sera un type équivalent à { vehicleList : Vehicle[] }
type  State = {
    [P in TListName] : T[]
}

// Nous créons une fonction qui crée l'état initial en initialisant un objet avec un tableau vide et le nom de la propriété donné
const initialState = (itemListName: TListName): State => {
    let result = {} as State;
    result[itemListName] = [];
    return result;
};

// Puisque nous ne pouvons pas utiliser l'opérateur de propagation, nous créons une nouvelle fonction qui met à jour l'état
// state sera l'état d'origine, 
// itemListName le nom de la propriété qui contient la liste 
// newItems sera la nouvelle liste 
const updateState = (args: { state: State, itemListName: TListName, newItems: T[] }): State => {
    return Object.assign({},args.state, {
        [args.itemListName] : args.newItems
    });
}
// Nous utiliserons une approche à 2 fonctions pour la fonction createReducer
// Nous faisons cela afin de pouvoir spécifier explicitement le type de l'élément (T),
// mais ne pas avoir à spécifier le type littéral de chaîne de caractères TListName et le laisser être inféré
const createReducer = (type: string) => (itemListName: TListName) => {
    return (state = initialState(itemListName), action: any): State => {
        switch (action.type) {
            case types.get(type).Add:
                return updateState({
                    state,
                    itemListName,
                    newItems: [...state[itemListName], action.payload]
                });

            case types.get(type).AddList:
                return updateState({
                    state,
                    itemListName,
                    newItems: [...state[itemListName], ...action.payload]
                })

            default:
                return state;
        }
    }
}
export const reducers = {
    vehiculeState: createReducer('vehicule')('vehiculeItems'),
    rentState: createReducer('rent')('rentItems'),
    clientState : createReducer('client')('clientItems'),
    companionState : createReducer('companion')('companionItems'),
    paymentState : createReducer('payment')('paymentItems'),
    notificationState : createReducer('notification')('notificationItems'),
    employeeState : createReducer('employee')('employeeItems'),
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X