Skip to content

Commit 8dc6118

Browse files
fix: dev extension
1 parent 1143e3f commit 8dc6118

2 files changed

Lines changed: 120 additions & 44 deletions

File tree

package-lock.json

Lines changed: 30 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/plugins/dev-extension/state-listener.ts

Lines changed: 90 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import _ from 'lodash';
2-
import { spy } from 'mobx';
2+
import { spy, untracked } from 'mobx';
33
import { ROOT_CONTEXT_ID } from '@src/constants';
44
import Manager from '../../manager';
55

66
enum Listeners {
77
SPY = 'spy',
88
}
99

10+
type SpyEvent = Parameters<Parameters<typeof spy>[0]>[0];
11+
1012
/**
1113
* State listener
1214
*/
@@ -22,6 +24,17 @@ class StateListener {
2224
*/
2325
protected static listeners: Record<Listeners | string, () => void> = {} as never;
2426

27+
/**
28+
* Last mobx event
29+
* @private
30+
*/
31+
private _lastEvent: SpyEvent | null = null;
32+
33+
/**
34+
* @private
35+
*/
36+
private readonly _throttleMs = 16;
37+
2538
/**
2639
* @constructor
2740
*/
@@ -52,50 +65,102 @@ class StateListener {
5265
}
5366

5467
/**
55-
* Get stores state
56-
* @protected
68+
* Store snapshot WITHOUT creating mobx dependencies
5769
*/
5870
protected getStoresState(): { root: Record<string, any> } {
59-
const state: { root: Record<string, any> } = { root: {} };
71+
return untracked(() => {
72+
const state: { root: Record<string, any> } = { root: {} };
6073

61-
try {
62-
const stores = this.manager.getStores();
74+
try {
75+
const stores = this.manager.getStores();
6376

64-
this.manager.getStoresRelations().forEach(({ ids, componentName }, contextId) => {
65-
const key = this.getContextKey(contextId);
77+
this.manager.getStoresRelations().forEach(({ ids, componentName }, contextId) => {
78+
const key = this.getContextKey(contextId);
6679

67-
ids.forEach((id) => {
68-
const store = stores.get(id);
80+
ids.forEach((id) => {
81+
const store = stores.get(id);
6982

70-
if (store) {
71-
const storeState = store?.toJSON?.() ?? Manager.getObservableProps(store);
83+
if (store) {
84+
const storeState = store?.toJSON?.() ?? Manager.getObservableProps(store);
7285

73-
_.set(state, `${key}.stores.${id}`, storeState);
74-
_.set(state, `${key}.componentName`, componentName);
75-
}
86+
_.set(state, `${key}.stores.${id}`, storeState);
87+
_.set(state, `${key}.componentName`, componentName);
88+
}
89+
});
7690
});
77-
});
78-
} catch (e) {
79-
// manager has not initialized yet
80-
}
91+
} catch {
92+
// manager has not initialized yet
93+
}
8194

82-
return state;
95+
return state;
96+
});
8397
}
8498

99+
/**
100+
* Safe clone
101+
* @private
102+
*/
103+
private getSafeEvent(event: SpyEvent): SpyEvent {
104+
const normalized = _.mapValues(event, (v) => {
105+
if (v == null) {
106+
return v;
107+
}
108+
109+
const t = typeof v;
110+
111+
if (t === 'string' || t === 'number' || t === 'boolean') {
112+
return v;
113+
}
114+
115+
if (Array.isArray(v)) {
116+
return { length: v.length };
117+
}
118+
119+
if (t === 'function' || t === 'symbol') {
120+
return undefined;
121+
}
122+
123+
if (t === 'object') {
124+
return { objectType: v?.constructor?.name ?? 'Object' };
125+
}
126+
127+
return undefined;
128+
});
129+
130+
return _.pickBy(normalized, (v) => v !== undefined) as SpyEvent;
131+
}
132+
133+
/**
134+
* Batch sending
135+
* @private
136+
*/
137+
private emitChange = _.throttle(
138+
() => {
139+
const payload = {
140+
event: this._lastEvent,
141+
storesState: this.getStoresState(),
142+
};
143+
144+
this.manager?.['__devOnChange']?.(payload);
145+
this._lastEvent = null;
146+
},
147+
this._throttleMs,
148+
{ leading: false, trailing: true },
149+
);
150+
85151
/**
86152
* Subscribe on stores changes
87153
* @protected
88154
*/
89155
public subscribe(): Manager {
90156
StateListener.listeners[Listeners.SPY] = spy((event) => {
91-
if (['report-end', 'reaction'].includes(event.type)) {
157+
if (['report-end', 'reaction'].includes(event?.type)) {
92158
return;
93159
}
94160

95-
this.manager?.['__devOnChange']?.({
96-
event: _.cloneDeep(event),
97-
storesState: this.getStoresState(),
98-
});
161+
this._lastEvent = this.getSafeEvent(event);
162+
163+
this.emitChange();
99164
});
100165

101166
return this.manager;

0 commit comments

Comments
 (0)