Skip to content

Commit 9c19923

Browse files
committed
fix(reactant-module): add destroy api for state subscriptions
1 parent 309b471 commit 9c19923

File tree

8 files changed

+80
-10
lines changed

8 files changed

+80
-10
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"cSpell.words": [
88
"inversify",
99
"middlewares",
10-
"persistor"
10+
"persistor",
11+
"unsubscriptions"
1112
]
1213
}

packages/reactant-module/src/constants/reduxKeys.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export const storeKey: unique symbol = Symbol('store');
22
export const loaderKey: unique symbol = Symbol('loader');
33
export const subscriptionsKey: unique symbol = Symbol('subscriptions');
4+
export const unsubscriptionsKey: unique symbol = Symbol('unsubscriptions');
45
export const stateKey: unique symbol = Symbol('state');
56
export const defaultStateKey: unique symbol = Symbol('defaultState');
67
export const enablePatchesKey: unique symbol = Symbol('enablePatches');

packages/reactant-module/src/core/subscribe.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */
22
import { Unsubscribe } from 'redux';
33
import { Subscribe } from '../interfaces';
4-
import { storeKey, subscriptionsKey } from '../constants';
4+
import { storeKey, subscriptionsKey, unsubscriptionsKey } from '../constants';
55

66
/**
77
* ## Description
@@ -57,17 +57,30 @@ const subscribe: Subscribe = (service, listener, options) => {
5757
unsubscribe = service[storeKey]?.subscribe(callback)!;
5858
} else {
5959
// When constructing
60-
const subscriptions = service[subscriptionsKey] || [];
60+
const subscriptions = service[subscriptionsKey] ?? [];
6161
let _unsubscribe: Unsubscribe;
6262
subscriptions.push(() => {
6363
_unsubscribe = service[storeKey]?.subscribe(callback)!;
6464
});
65-
unsubscribe = () => _unsubscribe();
65+
unsubscribe = () => {
66+
return _unsubscribe();
67+
};
6668
Object.assign(service, {
6769
[subscriptionsKey]: subscriptions,
6870
});
6971
}
70-
return unsubscribe!;
72+
const unsubscriptions = service[unsubscriptionsKey] ?? new Set();
73+
const fn = () => {
74+
unsubscribe();
75+
unsubscriptions.delete(fn);
76+
};
77+
unsubscriptions.add(fn);
78+
if (!service[unsubscriptionsKey]) {
79+
Object.assign(service, {
80+
[unsubscriptionsKey]: unsubscriptions,
81+
});
82+
}
83+
return fn;
7184
};
7285

7386
export { subscribe };

packages/reactant-module/src/interfaces.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
nameKey,
3030
modulesKey,
3131
initStateKey,
32+
unsubscriptionsKey,
3233
} from './constants';
3334
import { PluginModule } from './core';
3435

@@ -71,6 +72,7 @@ export interface Service<T extends Record<string, any> = Record<string, any>> {
7172
readonly [loaderKey]?: Loader;
7273
readonly [enablePatchesKey]?: boolean;
7374
readonly [subscriptionsKey]?: Subscriptions;
75+
readonly [unsubscriptionsKey]?: Set<Unsubscribe>;
7476
readonly [containerKey]?: Container;
7577
readonly [modulesKey]?: Record<string, any>;
7678
readonly [initStateKey]?: Record<string, any>;

packages/reactant-ssr/src/createServerApp.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type { ServerConfig, ServerApp } from './interfaces';
1212
export const createServerApp = <T, S extends any[], R extends Renderer<S>>(
1313
options: ServerConfig<T, S, R>
1414
): ServerApp<T, S, R> => {
15-
const { bootstrap, store, instance, container, modules } = createBaseApp({
15+
const { bootstrap, ...rest } = createBaseApp({
1616
...options,
1717
main: options.main ?? (AppView as Config<T, S, R>['main']),
1818
render: (element) => element,
@@ -25,10 +25,7 @@ export const createServerApp = <T, S extends any[], R extends Renderer<S>>(
2525
)) as JSX.Element;
2626
};
2727
return {
28-
store,
29-
instance,
30-
container,
31-
modules,
3228
bootstrap: AppComponent,
29+
...rest,
3330
};
3431
};

packages/reactant/src/createApp.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
Container,
1414
modulesKey,
1515
Service,
16+
unsubscriptionsKey,
1617
} from 'reactant-module';
1718
import { Config, App, Renderer } from './interfaces';
1819

@@ -128,6 +129,7 @@ function createApp<T, S extends any[], R extends Renderer<S>>({
128129
const instance = container.get<T>(
129130
typeof main === 'object' ? main.provide : main
130131
);
132+
131133
return {
132134
/**
133135
* App's main module instance.
@@ -145,6 +147,21 @@ function createApp<T, S extends any[], R extends Renderer<S>>({
145147
* all modules collection
146148
*/
147149
modules: ((instance as any) as Service)[modulesKey]!,
150+
/**
151+
* destroy all subscriptions
152+
*/
153+
destroy: () => {
154+
const modulesMap = ((instance as any) as Service)[modulesKey]!;
155+
Object.keys(modulesMap).forEach((key) => {
156+
const module = modulesMap[key] as Service | null | undefined;
157+
const unsubscriptions = module?.[unsubscriptionsKey];
158+
if (unsubscriptions) {
159+
for (const unsubscribe of unsubscriptions) {
160+
unsubscribe();
161+
}
162+
}
163+
});
164+
},
148165
/**
149166
* Bootstrap app with a renderer.
150167
*/

packages/reactant/src/interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export interface App<T, S extends any[], R extends Renderer<S>> {
4444
container: Container;
4545
store: ReactantStore | null;
4646
modules: Record<string, any>;
47+
destroy: () => void;
4748
bootstrap(...args: S): ReturnType<R> | Promise<R>;
4849
}
4950

packages/reactant/test/integration/index.test.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-shadow */
12
/* eslint-disable import/no-extraneous-dependencies */
23
/* eslint-disable @typescript-eslint/no-unused-vars */
34
/* eslint-disable @typescript-eslint/ban-ts-ignore */
@@ -26,6 +27,8 @@ import {
2627
optional,
2728
inject,
2829
autobind,
30+
subscribe,
31+
watch,
2932
} from '../..';
3033

3134
let container: Element;
@@ -168,6 +171,9 @@ describe('base API', () => {
168171
const sum1ComputedFn = jest.fn();
169172
const getPropsFn = jest.fn();
170173

174+
const subscribeFn = jest.fn();
175+
const watchFn = jest.fn();
176+
171177
interface HomeView1Props {
172178
version?: string;
173179
text?: string;
@@ -236,6 +242,12 @@ describe('base API', () => {
236242
name: 'homeView',
237243
})
238244
class HomeView extends HomeView1 {
245+
constructor() {
246+
super();
247+
subscribe(this, subscribeFn);
248+
watch(this, () => this.state.list[0].count, watchFn);
249+
}
250+
239251
@action
240252
increase(num: number) {
241253
super.increase(num);
@@ -393,6 +405,32 @@ describe('base API', () => {
393405
container = document.createElement('div');
394406
document.body.appendChild(container);
395407

408+
const subscribeFn1 = jest.fn();
409+
const watchFn1 = jest.fn();
410+
411+
subscribe(app.instance.homeView, subscribeFn1);
412+
watch(
413+
app.instance.homeView,
414+
() => app.instance.homeView.state.list[0].count,
415+
watchFn1
416+
);
417+
418+
expect(subscribeFn).toBeCalledTimes(4);
419+
expect(watchFn).toBeCalledTimes(3);
420+
expect(subscribeFn1).toBeCalledTimes(0);
421+
expect(watchFn1).toBeCalledTimes(0);
422+
app.instance.homeView.increase(1);
423+
expect(subscribeFn).toBeCalledTimes(5);
424+
expect(watchFn).toBeCalledTimes(4);
425+
expect(subscribeFn1).toBeCalledTimes(1);
426+
expect(watchFn1).toBeCalledTimes(1);
427+
app.destroy();
428+
app.instance.homeView.increase(1);
429+
expect(subscribeFn).toBeCalledTimes(5);
430+
expect(watchFn).toBeCalledTimes(4);
431+
expect(subscribeFn1).toBeCalledTimes(1);
432+
expect(watchFn1).toBeCalledTimes(1);
433+
396434
const app1 = createApp({
397435
main: HomeView,
398436
render,

0 commit comments

Comments
 (0)