'RXJS subscription handling - Code simplification

I'll start by spilling code down, following by a small explanation and then my question

Vue3, typescript

I've got an RXJS Wrapper That enables me to handle my subscriptions and events using Subjects in specific namespaces so that I can emit certain events in certain "channels" and thus handle my data in a more organized matter - an event bus of sorts.

export class Rxjs {
    private static _instance: Rxjs;
    system: RxjsInstance;
    view: RxjsInstance;
    popup: RxjsInstance;
    data: RxjsInstance;
    assets: RxjsInstance;
    collections: RxjsInstance;
    account: RxjsInstance;
    blockchain: RxjsInstance;
    logs: RxjsInstance;
    
    private constructor() {
        this.system = new RxjsInstance("behaviorSubject");
        this.blockchain = new RxjsInstance("behaviorSubject");
        this.view = new RxjsInstance("behaviorSubject");
        this.popup = new RxjsInstance("behaviorSubject");
        this.data = new RxjsInstance();
        this.assets = new RxjsInstance();
        this.collections = new RxjsInstance();
        this.account = new RxjsInstance();
        this.logs = new RxjsInstance();
    }
    
    static next = (namespace: string, rxjsPayload: System.IRxjsPayload) => {
        const rxjs = Rxjs.getInstance();
        const instances: System.IAny = {
            "system": rxjs.system,
            "blockchain": rxjs.blockchain,
            "view": rxjs.view,
            "popup": rxjs.popup,
            "data": rxjs.data,
            "assets": rxjs.assets,
            "collections": rxjs.collections,
            "account": rxjs.account,
            "logs": rxjs.logs
        };
        instances[namespace].next(rxjsPayload);
    };
    
    static subscribe = (namespace: string, listener: ({ cta, data }: System.IRxjsPayload) => void) => {
        const rxjs = Rxjs.getInstance();
        const instances: System.IAny = {
            "system": rxjs.system,
            "blockchain": rxjs.blockchain,
            "view": rxjs.view,
            "popup": rxjs.popup,
            "data": rxjs.data,
            "assets": rxjs.assets,
            "collections": rxjs.collections,
            "account": rxjs.account,
            "logs": rxjs.logs
        };
        return instances[namespace].subscribe((payload: System.IRxjsPayload) => listener(payload));
    };
    
    public static getInstance = (): Rxjs => {
        if (!Rxjs._instance) Rxjs._instance = new Rxjs();
        return Rxjs._instance;
    };
}

class RxjsInstance {
    subscriptions = new Subscription();
    subject: any;
    
    constructor(subjectType: string = "subject") {
        switch (subjectType) {
            case "subject":
                this.subject = new Subject();
                break;
            case "asyncSubject":
                this.subject = new AsyncSubject();
                break;
            case "behaviorSubject":
                this.subject = new BehaviorSubject(null);
                break;
            case "replaySubject":
                this.subject = new ReplaySubject();
                break;
        }
    }
    
    next({ cta, data }: System.IRxjsPayload): void {
        const validAction = MetamaskService.actionIsAllowed(window.ethereum, cta);
        if (validAction) {
            this.subject.next({ cta: cta, data: data });
        }
    }
    
    asObservable(): any {
        return this.subject.asObservable();
    }
    
    set add(subscription: Subscription) {
        this.subscriptions.add(subscription);
    }
    
    subscribe(listener: ({ cta, data }: System.IRxjsPayload) => void): Subscription {
        if (typeof listener !== "function") {
            throw "Failed to subscribe. Please provide a listener function";
        }
        let subscriber = this.asObservable().subscribe((payload: System.IRxjsPayload) => {
            if (!payload || !payload.hasOwnProperty("cta")) return;
            listener(payload);
        });
        this.subscriptions.add(subscriber);
        return subscriber;
    }
    
    unsubscribe() {
        this.subscriptions.unsubscribe();
    }
}

I then subscribe to a namespace

state.sub.collections = rxjs.collections.subscribe(({ cta, data }: System.IRxjsPayload) => {
                switch (cta) {
                    case E_RXJS.RESET_COLLECTIONS:
                        commit("setProperty", { key: "collections", value: [] });
                        commit("setProperty", { key: "collection", value: null });
                        break;
                }
            });

"cta" is a "call to action" which indicates what I want to do - data is w/e payload I want.

but then I came to the realization that I've got a bit too many namespaces and it becomes annoying handling them all - so I created a "facade" next and subscribe functions in my base Rxjs class (above) which receives a namespace and does basically the same thing but enables me to change my syntax to the following.

create the subscriptions based on a sub object that contains keys of namespaces I want to register to.

            const blockchainActions: System.IAny = {
                [E_RXJS.PROCESSING]: async (data: any) => {
                    context.commit("setProperty", { key: "processing", value: data });
                },
                
                [E_RXJS.CONNECTING]: async (data: any) => {
                    context.commit("setProperty", { key: "connecting", value: data });
                },
                
                [E_RXJS.BLOCKCHAIN_UPDATE_TOKEN_RATE]: async (data: any) => {
                    context.commit("setProperty", { key: "paymentToken", value: { symbol: data.symbol, rate: data.rate, params: data.params } });
                },
                
                [E_RXJS.BLOCKCHAIN_SET_NETWORK]: async (data: any) => {
                    context.commit("setProperty", { key: "networkParams", value: data });
                }
            };
            const dataActions: System.IAny = {};
            for (let key in context.state.sub) {
                context.state.sub[key] = Rxjs.subscribe(key, async ({ cta, data }) => {
                    let actions: System.IAny = {};
                    switch (key) {
                        case "account":
                            actions = accountActions;
                            break;
                        case "blockchain":
                            actions = blockchainActions;
                            break;
                        case "system":
                            actions = systemActions;
                            break;
                        case "data":
                            actions = dataActions;
                            break;
                    }
                    (await actions[cta] || (() => {
                    }))(data);
                });
            }
    

The thing I want is to find a way to make the last "switch case" section even shorter. I don't want to do a switch case but I cant find a way to simplify my code even further...

any ideas?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source