'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 |
|---|
