Skip to content

Commit

Permalink
feat(store): support auto unregister store #INFR-6873 (#110)
Browse files Browse the repository at this point in the history
  • Loading branch information
helingkaih authored Mar 17, 2023
1 parent 540e6e0 commit 648cb85
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 12 deletions.
2 changes: 1 addition & 1 deletion .docgeni/public/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"esModuleInterop": false,
"importHelpers": true,
"target": "es2015",
"lib": ["es2018", "dom"],
"lib": ["esnext", "dom"],
"types": ["node"],
"paths": {
"@tethys/store/*": ["../../packages/store/*"],
Expand Down
49 changes: 41 additions & 8 deletions packages/store/internals/internal-store-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,64 @@ export class InternalStoreFactory implements OnDestroy {
return this.factory;
}

private storeInstancesMap = new Map<string, Store>();
private storeInstancesMap = new Map<string, WeakRef<Store>>();

public state$ = new Subject<{ storeId: string; state: unknown }>();

register(store: Store) {
this.storeInstancesMap.set(store.getStoreInstanceId(), store);
this.storeInstancesMap.set(store.getStoreInstanceId(), new WeakRef(store));
}

unregister(store: Store) {
this.storeInstancesMap.delete(store.getStoreInstanceId());
}

get(id: string) {
return this.storeInstancesMap.get(id);
const storeWeakRef = this.storeInstancesMap.get(id);
if (storeWeakRef) {
const store = storeWeakRef.deref();
if (store) {
return store;
} else {
this.storeInstancesMap.delete(id);
return null;
}
} else {
return null;
}
}

getStores(predicate: (storeId: string, name: string, store?: string) => boolean) {
const stores = [];
this.storeInstancesMap.forEach((storeWeakRef, id) => {
const store = storeWeakRef.deref();
if (!store) {
this.storeInstancesMap.delete(id);
return;
}
if (predicate(id, store.getName())) {
stores.push(store);
}
});
return stores;
}

getAllStores() {
return this.getStores((storeId: string, name: string) => {
return true;
});
}

getStores(names: string | string[]) {
return Array.from(this.storeInstancesMap.values()).filter((store) => {
return coerceArray(names).includes(store.getName());
getStoresByNames(names: string | string[]) {
names = coerceArray(names);
return this.getStores((storeId: string, name: string) => {
return names.includes(name);
});
}

getAllState() {
return Array.from(this.storeInstancesMap.entries()).reduce((state, [storeId, store]) => {
state[storeId] = store.getState();
return this.getAllStores().reduce((state, store) => {
state[store.getStoreInstanceId()] = store.getState();
return state;
}, {});
}
Expand Down
2 changes: 1 addition & 1 deletion packages/store/store-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ export class StoreFactory {
constructor() {}

getStores<T = Store>(names: string | string[]): T[] {
return InternalStoreFactory.instance.getStores(names) as unknown as T[];
return InternalStoreFactory.instance.getStoresByNames(names) as unknown as T[];
}
}
23 changes: 23 additions & 0 deletions packages/store/test/store-destroy.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { InternalStoreFactory } from '../internals/internal-store-factory';
import { Store } from '../store';

class GardenState {
Expand Down Expand Up @@ -32,4 +33,26 @@ describe('store-auto-destroy', () => {
expect(store).toBeDefined();
expect(store.getStoreInstanceId()).toBe('GardenStore');
});

it('should store destroy', () => {
let store = TestBed.inject(GardenStore);
const storeName = store.getName();
expect(store).toBeDefined();
expect(InternalStoreFactory.instance.get(storeName)).toBeTruthy();
expect(InternalStoreFactory.instance.getStoresByNames(storeName)).toBeTruthy();
store.ngOnDestroy();
expect(InternalStoreFactory.instance.get(storeName)).toBeFalsy();
});

it('should weakRef effective', () => {
InternalStoreFactory.instance['storeInstancesMap'] = new Map<string, WeakRef<Store>>();
const store = TestBed.inject(GardenStore);
const storeName = store.getName();
expect(store).toBeDefined();
expect(InternalStoreFactory.instance.getStoresByNames(storeName)).toBeTruthy();
expect(InternalStoreFactory.instance['storeInstancesMap'].size).toBe(1);
spyOn(InternalStoreFactory.instance['storeInstancesMap'].get(storeName), 'deref').and.returnValue(null);
expect(InternalStoreFactory.instance.getStoresByNames(storeName)).toEqual([]);
expect(InternalStoreFactory.instance['storeInstancesMap'].size).toBe(0);
});
});
2 changes: 1 addition & 1 deletion packages/store/tsconfig.lib.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"declarationMap": true,
"inlineSources": true,
"types": [],
"lib": ["dom", "es2018"]
"lib": ["dom", "esnext"]
},
"angularCompilerOptions": {
"skipTemplateCodegen": true,
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"importHelpers": true,
"target": "ES2022",
"module": "es2020",
"lib": ["es2018", "dom"],
"lib": ["esnext", "dom"],
"useDefineForClassFields": false
},
"angularCompilerOptions": {
Expand Down

0 comments on commit 648cb85

Please sign in to comment.