@taicode/common-web 1.1.6 → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../source/service/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,KAA2D,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../source/service/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,KAA2D,MAAM,OAAO,CAAA;AA+D/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;AAClD,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;AAgDlF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;AAC9E,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;AAyF9G;;GAEG;AACH,UAAU,oBAAoB;IAC5B,sBAAsB;IACtB,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAA;IAC7B,UAAU;IACV,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,6CAA6C;AAE7C,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,8EAuB1D"}
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
35
|
import { reaction, runInAction } from 'mobx';
|
|
36
|
-
import { catchIt } from '@taicode/common-base';
|
|
37
36
|
import { Container } from '@needle-di/core';
|
|
38
37
|
import React, { useContext, useEffect, useMemo, useState, useRef } from 'react';
|
|
39
38
|
/**
|
|
@@ -41,6 +40,50 @@ import React, { useContext, useEffect, useMemo, useState, useRef } from 'react';
|
|
|
41
40
|
* 通过上下文在组件树中共享依赖注入容器
|
|
42
41
|
*/
|
|
43
42
|
const ctx = React.createContext(undefined);
|
|
43
|
+
const INIT_RETURN_ERROR_MESSAGE = 'Service init must return a boolean value.';
|
|
44
|
+
class InvalidInitReturnError extends Error {
|
|
45
|
+
constructor() {
|
|
46
|
+
super(INIT_RETURN_ERROR_MESSAGE);
|
|
47
|
+
this.name = 'InvalidInitReturnError';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function setServiceInited(target, value) {
|
|
51
|
+
runInAction(() => {
|
|
52
|
+
if (target != null && typeof target === 'object') {
|
|
53
|
+
target.inited = value;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function applyInitResult(target, result) {
|
|
58
|
+
if (typeof result === 'boolean') {
|
|
59
|
+
setServiceInited(target, result);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
setServiceInited(target, false);
|
|
63
|
+
throw new InvalidInitReturnError();
|
|
64
|
+
}
|
|
65
|
+
async function initializeServiceInstance(target) {
|
|
66
|
+
if (target == null ||
|
|
67
|
+
typeof target !== 'object' ||
|
|
68
|
+
!('init' in target) ||
|
|
69
|
+
typeof target.init !== 'function') {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const service = target;
|
|
73
|
+
if (service.inited === true) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const result = await service.init();
|
|
78
|
+
applyInitResult(service, result);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (error instanceof InvalidInitReturnError) {
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
setServiceInited(service, false);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
44
87
|
export function useService(target, selector) {
|
|
45
88
|
const container = useContext(ctx);
|
|
46
89
|
// 使用 useState 强制组件重新渲染
|
|
@@ -84,6 +127,7 @@ export function useLocalService(ServiceClass, selector) {
|
|
|
84
127
|
const serviceInstanceRef = useRef(null);
|
|
85
128
|
// 使用 useState 强制组件重新渲染,完全模仿 useService
|
|
86
129
|
const [, refresh] = useState({});
|
|
130
|
+
const [initError, setInitError] = useState(null);
|
|
87
131
|
if (parentContainer == null) {
|
|
88
132
|
throw new Error('Must be a child of ServiceProvider.');
|
|
89
133
|
}
|
|
@@ -125,34 +169,21 @@ export function useLocalService(ServiceClass, selector) {
|
|
|
125
169
|
React.useEffect(() => {
|
|
126
170
|
if (parentContainer == null)
|
|
127
171
|
return;
|
|
172
|
+
let disposed = false;
|
|
173
|
+
setInitError(prev => (prev === null ? prev : null));
|
|
128
174
|
const service = serviceInstanceRef.current;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
'init' in service &&
|
|
133
|
-
typeof service.init === 'function') {
|
|
134
|
-
// 直接调用 init 方法
|
|
135
|
-
const initResult = service.init();
|
|
136
|
-
if (initResult && typeof initResult.then === 'function') {
|
|
137
|
-
// 异步初始化
|
|
138
|
-
initResult.then((result) => {
|
|
139
|
-
runInAction(() => {
|
|
140
|
-
service.inited = result;
|
|
141
|
-
});
|
|
142
|
-
}).catch((error) => {
|
|
143
|
-
runInAction(() => {
|
|
144
|
-
service.inited = false;
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
// 同步初始化
|
|
150
|
-
runInAction(() => {
|
|
151
|
-
service.inited = true;
|
|
152
|
-
});
|
|
175
|
+
initializeServiceInstance(service).catch((error) => {
|
|
176
|
+
if (error instanceof InvalidInitReturnError && !disposed) {
|
|
177
|
+
setInitError(error);
|
|
153
178
|
}
|
|
154
|
-
}
|
|
179
|
+
});
|
|
180
|
+
return () => {
|
|
181
|
+
disposed = true;
|
|
182
|
+
};
|
|
155
183
|
}, [parentContainer, ServiceClass]); // 注意:这里不依赖 selector
|
|
184
|
+
if (initError) {
|
|
185
|
+
throw initError;
|
|
186
|
+
}
|
|
156
187
|
// 返回选择器结果或服务实例
|
|
157
188
|
return selector ? selector(serviceInstanceRef.current) : serviceInstanceRef.current;
|
|
158
189
|
}
|
|
@@ -228,32 +259,38 @@ export function ServiceProvider(props) {
|
|
|
228
259
|
function InitService(props) {
|
|
229
260
|
const { services, children } = props;
|
|
230
261
|
const container = useContext(ctx);
|
|
262
|
+
const [initError, setInitError] = React.useState(null);
|
|
231
263
|
useEffect(() => {
|
|
232
264
|
if (container == null)
|
|
233
265
|
return;
|
|
234
266
|
if (services.length == 0)
|
|
235
267
|
return;
|
|
268
|
+
let disposed = false;
|
|
269
|
+
setInitError(prev => (prev === null ? prev : null));
|
|
236
270
|
// 异步初始化所有服务
|
|
237
|
-
(async () => {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if (
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
runInAction(() => instance.inited = !result.isError());
|
|
250
|
-
}
|
|
271
|
+
const initPromises = services.map(async (service) => {
|
|
272
|
+
// 尝试从容器中获取服务实例(可选的,避免未注册时报错)
|
|
273
|
+
const instance = container.get(service, { optional: true });
|
|
274
|
+
if (instance != null && typeof instance === 'object') {
|
|
275
|
+
// 检查实例是否实现了 Service 接口
|
|
276
|
+
if ('init' in instance && typeof instance.init === 'function') {
|
|
277
|
+
if (instance.inited !== true) {
|
|
278
|
+
await initializeServiceInstance(instance).catch((error) => {
|
|
279
|
+
if (error instanceof InvalidInitReturnError && !disposed) {
|
|
280
|
+
setInitError(error);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
251
283
|
}
|
|
252
284
|
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
void Promise.all(initPromises);
|
|
288
|
+
return () => {
|
|
289
|
+
disposed = true;
|
|
290
|
+
};
|
|
257
291
|
}, [container, services]);
|
|
292
|
+
if (initError) {
|
|
293
|
+
throw initError;
|
|
294
|
+
}
|
|
258
295
|
return children;
|
|
259
296
|
}
|
|
@@ -44,12 +44,30 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
44
44
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
45
45
|
};
|
|
46
46
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
47
|
-
import { act } from 'react';
|
|
47
|
+
import React, { act } from 'react';
|
|
48
48
|
import { Service } from '@taicode/common-base';
|
|
49
49
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
50
50
|
import { render, renderHook, waitFor } from '@testing-library/react';
|
|
51
51
|
import { observable, makeObservable, runInAction, action } from 'mobx';
|
|
52
52
|
import { useService, useLocalService, ServiceProvider } from './service';
|
|
53
|
+
class TestErrorBoundary extends React.Component {
|
|
54
|
+
constructor() {
|
|
55
|
+
super(...arguments);
|
|
56
|
+
this.state = { error: null };
|
|
57
|
+
}
|
|
58
|
+
static getDerivedStateFromError(error) {
|
|
59
|
+
return { error };
|
|
60
|
+
}
|
|
61
|
+
componentDidCatch(error) {
|
|
62
|
+
this.props.onError(error);
|
|
63
|
+
}
|
|
64
|
+
render() {
|
|
65
|
+
if (this.state.error) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
return this.props.children;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
53
71
|
// 创建测试用的服务类
|
|
54
72
|
let TestService = (() => {
|
|
55
73
|
var _a, _TestService_count_accessor_storage, _TestService_users_accessor_storage, _TestService_inited_accessor_storage;
|
|
@@ -369,6 +387,63 @@ describe('service integration', () => {
|
|
|
369
387
|
expect(result.current.count).toBe(0);
|
|
370
388
|
expect(result.current.value).toBe('initial');
|
|
371
389
|
});
|
|
390
|
+
it('在未提供 selector 时应该返回完整服务实例并响应更新', async () => {
|
|
391
|
+
const TestProvider = createTestProvider([TestService]);
|
|
392
|
+
const { result } = renderHook(() => useService(TestService), { wrapper: TestProvider });
|
|
393
|
+
const firstInstance = result.current;
|
|
394
|
+
expect(firstInstance.count).toBe(0);
|
|
395
|
+
act(() => {
|
|
396
|
+
firstInstance.increment();
|
|
397
|
+
});
|
|
398
|
+
await waitFor(() => {
|
|
399
|
+
expect(result.current.count).toBe(1);
|
|
400
|
+
});
|
|
401
|
+
expect(result.current).toBe(firstInstance);
|
|
402
|
+
});
|
|
403
|
+
it('当 selector 返回服务实例时应该退化为监听全部属性', async () => {
|
|
404
|
+
const TestProvider = createTestProvider([TestService]);
|
|
405
|
+
const { result } = renderHook(() => ({
|
|
406
|
+
serviceViaSelector: useService(TestService, service => service),
|
|
407
|
+
counter: useService(TestService, service => service.count)
|
|
408
|
+
}), { wrapper: TestProvider });
|
|
409
|
+
const initialServiceRef = result.current.serviceViaSelector;
|
|
410
|
+
expect(initialServiceRef.count).toBe(0);
|
|
411
|
+
act(() => {
|
|
412
|
+
initialServiceRef.increment();
|
|
413
|
+
});
|
|
414
|
+
await waitFor(() => {
|
|
415
|
+
expect(result.current.counter).toBe(1);
|
|
416
|
+
expect(result.current.serviceViaSelector.count).toBe(1);
|
|
417
|
+
});
|
|
418
|
+
expect(result.current.serviceViaSelector).toBe(initialServiceRef);
|
|
419
|
+
});
|
|
420
|
+
it('useService 的 selector 应该能够响应其他服务的 observable 变化', async () => {
|
|
421
|
+
const TestProvider = createTestProvider([TestService, AnotherService]);
|
|
422
|
+
const { result } = renderHook(() => {
|
|
423
|
+
const globalService = useService(TestService, service => service);
|
|
424
|
+
const anotherSelection = useService(AnotherService, service => ({
|
|
425
|
+
service,
|
|
426
|
+
combined: `${service.value}-${globalService.count}`
|
|
427
|
+
}));
|
|
428
|
+
return {
|
|
429
|
+
globalService,
|
|
430
|
+
anotherSelection
|
|
431
|
+
};
|
|
432
|
+
}, { wrapper: TestProvider });
|
|
433
|
+
expect(result.current.anotherSelection.combined).toBe('initial-0');
|
|
434
|
+
act(() => {
|
|
435
|
+
result.current.anotherSelection.service.setValue('changed');
|
|
436
|
+
});
|
|
437
|
+
await waitFor(() => {
|
|
438
|
+
expect(result.current.anotherSelection.combined).toBe('changed-0');
|
|
439
|
+
});
|
|
440
|
+
act(() => {
|
|
441
|
+
result.current.globalService.increment();
|
|
442
|
+
});
|
|
443
|
+
await waitFor(() => {
|
|
444
|
+
expect(result.current.anotherSelection.combined).toBe('changed-1');
|
|
445
|
+
});
|
|
446
|
+
});
|
|
372
447
|
});
|
|
373
448
|
describe('ServiceProvider', () => {
|
|
374
449
|
it('应该能够渲染子组件', () => {
|
|
@@ -446,6 +521,207 @@ describe('service integration', () => {
|
|
|
446
521
|
expect(result.current).toBe(false); // 因为初始化失败,inited 应该保持 false
|
|
447
522
|
});
|
|
448
523
|
});
|
|
524
|
+
it('在服务已初始化时不会重复调用 init', async () => {
|
|
525
|
+
const initSpy = vi.fn(async () => true);
|
|
526
|
+
let PreInitedService = (() => {
|
|
527
|
+
var _a, _PreInitedService_inited_accessor_storage;
|
|
528
|
+
let _classSuper = Service;
|
|
529
|
+
let _inited_decorators;
|
|
530
|
+
let _inited_initializers = [];
|
|
531
|
+
let _inited_extraInitializers = [];
|
|
532
|
+
return _a = class PreInitedService extends _classSuper {
|
|
533
|
+
get inited() { return __classPrivateFieldGet(this, _PreInitedService_inited_accessor_storage, "f"); }
|
|
534
|
+
set inited(value) { __classPrivateFieldSet(this, _PreInitedService_inited_accessor_storage, value, "f"); }
|
|
535
|
+
constructor() {
|
|
536
|
+
super();
|
|
537
|
+
_PreInitedService_inited_accessor_storage.set(this, __runInitializers(this, _inited_initializers, true));
|
|
538
|
+
this.init = (__runInitializers(this, _inited_extraInitializers), initSpy);
|
|
539
|
+
makeObservable(this);
|
|
540
|
+
}
|
|
541
|
+
},
|
|
542
|
+
_PreInitedService_inited_accessor_storage = new WeakMap(),
|
|
543
|
+
(() => {
|
|
544
|
+
var _b;
|
|
545
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_b = _classSuper[Symbol.metadata]) !== null && _b !== void 0 ? _b : null) : void 0;
|
|
546
|
+
_inited_decorators = [observable];
|
|
547
|
+
__esDecorate(_a, null, _inited_decorators, { kind: "accessor", name: "inited", static: false, private: false, access: { has: obj => "inited" in obj, get: obj => obj.inited, set: (obj, value) => { obj.inited = value; } }, metadata: _metadata }, _inited_initializers, _inited_extraInitializers);
|
|
548
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
549
|
+
})(),
|
|
550
|
+
_a;
|
|
551
|
+
})();
|
|
552
|
+
const TestProvider = createTestProvider([PreInitedService]);
|
|
553
|
+
const { result } = renderHook(() => useService(PreInitedService, service => ({
|
|
554
|
+
inited: service.inited,
|
|
555
|
+
service
|
|
556
|
+
})), { wrapper: TestProvider });
|
|
557
|
+
await waitFor(() => {
|
|
558
|
+
expect(result.current.inited).toBe(true);
|
|
559
|
+
});
|
|
560
|
+
expect(initSpy).not.toHaveBeenCalled();
|
|
561
|
+
});
|
|
562
|
+
it('应该正确处理同步 init 的服务', async () => {
|
|
563
|
+
let SyncInitService = (() => {
|
|
564
|
+
var _a, _SyncInitService_inited_accessor_storage, _SyncInitService_value_accessor_storage;
|
|
565
|
+
let _inited_decorators;
|
|
566
|
+
let _inited_initializers = [];
|
|
567
|
+
let _inited_extraInitializers = [];
|
|
568
|
+
let _value_decorators;
|
|
569
|
+
let _value_initializers = [];
|
|
570
|
+
let _value_extraInitializers = [];
|
|
571
|
+
return _a = class SyncInitService {
|
|
572
|
+
get inited() { return __classPrivateFieldGet(this, _SyncInitService_inited_accessor_storage, "f"); }
|
|
573
|
+
set inited(value) { __classPrivateFieldSet(this, _SyncInitService_inited_accessor_storage, value, "f"); }
|
|
574
|
+
get value() { return __classPrivateFieldGet(this, _SyncInitService_value_accessor_storage, "f"); }
|
|
575
|
+
set value(value) { __classPrivateFieldSet(this, _SyncInitService_value_accessor_storage, value, "f"); }
|
|
576
|
+
constructor() {
|
|
577
|
+
_SyncInitService_inited_accessor_storage.set(this, __runInitializers(this, _inited_initializers, false));
|
|
578
|
+
_SyncInitService_value_accessor_storage.set(this, (__runInitializers(this, _inited_extraInitializers), __runInitializers(this, _value_initializers, 'pending')));
|
|
579
|
+
__runInitializers(this, _value_extraInitializers);
|
|
580
|
+
makeObservable(this);
|
|
581
|
+
}
|
|
582
|
+
init() {
|
|
583
|
+
runInAction(() => {
|
|
584
|
+
this.value = 'ready';
|
|
585
|
+
this.inited = true;
|
|
586
|
+
});
|
|
587
|
+
return true;
|
|
588
|
+
}
|
|
589
|
+
},
|
|
590
|
+
_SyncInitService_inited_accessor_storage = new WeakMap(),
|
|
591
|
+
_SyncInitService_value_accessor_storage = new WeakMap(),
|
|
592
|
+
(() => {
|
|
593
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
594
|
+
_inited_decorators = [observable];
|
|
595
|
+
_value_decorators = [observable];
|
|
596
|
+
__esDecorate(_a, null, _inited_decorators, { kind: "accessor", name: "inited", static: false, private: false, access: { has: obj => "inited" in obj, get: obj => obj.inited, set: (obj, value) => { obj.inited = value; } }, metadata: _metadata }, _inited_initializers, _inited_extraInitializers);
|
|
597
|
+
__esDecorate(_a, null, _value_decorators, { kind: "accessor", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
|
|
598
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
599
|
+
})(),
|
|
600
|
+
_a;
|
|
601
|
+
})();
|
|
602
|
+
const TestProvider = createTestProvider([SyncInitService]);
|
|
603
|
+
const { result } = renderHook(() => useService(SyncInitService, service => ({
|
|
604
|
+
inited: service.inited,
|
|
605
|
+
value: service.value
|
|
606
|
+
})), { wrapper: TestProvider });
|
|
607
|
+
await waitFor(() => {
|
|
608
|
+
expect(result.current.inited).toBe(true);
|
|
609
|
+
expect(result.current.value).toBe('ready');
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
it('应该为多个服务分别调用 init', async () => {
|
|
613
|
+
const firstInit = vi.fn(async () => true);
|
|
614
|
+
const secondInit = vi.fn(async () => true);
|
|
615
|
+
let FirstInitService = (() => {
|
|
616
|
+
var _a, _FirstInitService_inited_accessor_storage;
|
|
617
|
+
let _classSuper = Service;
|
|
618
|
+
let _inited_decorators;
|
|
619
|
+
let _inited_initializers = [];
|
|
620
|
+
let _inited_extraInitializers = [];
|
|
621
|
+
return _a = class FirstInitService extends _classSuper {
|
|
622
|
+
get inited() { return __classPrivateFieldGet(this, _FirstInitService_inited_accessor_storage, "f"); }
|
|
623
|
+
set inited(value) { __classPrivateFieldSet(this, _FirstInitService_inited_accessor_storage, value, "f"); }
|
|
624
|
+
constructor() {
|
|
625
|
+
super();
|
|
626
|
+
_FirstInitService_inited_accessor_storage.set(this, __runInitializers(this, _inited_initializers, false));
|
|
627
|
+
this.init = (__runInitializers(this, _inited_extraInitializers), firstInit);
|
|
628
|
+
makeObservable(this);
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
_FirstInitService_inited_accessor_storage = new WeakMap(),
|
|
632
|
+
(() => {
|
|
633
|
+
var _b;
|
|
634
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_b = _classSuper[Symbol.metadata]) !== null && _b !== void 0 ? _b : null) : void 0;
|
|
635
|
+
_inited_decorators = [observable];
|
|
636
|
+
__esDecorate(_a, null, _inited_decorators, { kind: "accessor", name: "inited", static: false, private: false, access: { has: obj => "inited" in obj, get: obj => obj.inited, set: (obj, value) => { obj.inited = value; } }, metadata: _metadata }, _inited_initializers, _inited_extraInitializers);
|
|
637
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
638
|
+
})(),
|
|
639
|
+
_a;
|
|
640
|
+
})();
|
|
641
|
+
let SecondInitService = (() => {
|
|
642
|
+
var _a, _SecondInitService_inited_accessor_storage;
|
|
643
|
+
let _classSuper = Service;
|
|
644
|
+
let _inited_decorators;
|
|
645
|
+
let _inited_initializers = [];
|
|
646
|
+
let _inited_extraInitializers = [];
|
|
647
|
+
return _a = class SecondInitService extends _classSuper {
|
|
648
|
+
get inited() { return __classPrivateFieldGet(this, _SecondInitService_inited_accessor_storage, "f"); }
|
|
649
|
+
set inited(value) { __classPrivateFieldSet(this, _SecondInitService_inited_accessor_storage, value, "f"); }
|
|
650
|
+
constructor() {
|
|
651
|
+
super();
|
|
652
|
+
_SecondInitService_inited_accessor_storage.set(this, __runInitializers(this, _inited_initializers, false));
|
|
653
|
+
this.init = (__runInitializers(this, _inited_extraInitializers), secondInit);
|
|
654
|
+
makeObservable(this);
|
|
655
|
+
}
|
|
656
|
+
},
|
|
657
|
+
_SecondInitService_inited_accessor_storage = new WeakMap(),
|
|
658
|
+
(() => {
|
|
659
|
+
var _b;
|
|
660
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_b = _classSuper[Symbol.metadata]) !== null && _b !== void 0 ? _b : null) : void 0;
|
|
661
|
+
_inited_decorators = [observable];
|
|
662
|
+
__esDecorate(_a, null, _inited_decorators, { kind: "accessor", name: "inited", static: false, private: false, access: { has: obj => "inited" in obj, get: obj => obj.inited, set: (obj, value) => { obj.inited = value; } }, metadata: _metadata }, _inited_initializers, _inited_extraInitializers);
|
|
663
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
664
|
+
})(),
|
|
665
|
+
_a;
|
|
666
|
+
})();
|
|
667
|
+
const TestProvider = createTestProvider([FirstInitService, SecondInitService]);
|
|
668
|
+
const { result } = renderHook(() => ({
|
|
669
|
+
firstInited: useService(FirstInitService, service => service.inited),
|
|
670
|
+
secondInited: useService(SecondInitService, service => service.inited)
|
|
671
|
+
}), { wrapper: TestProvider });
|
|
672
|
+
await waitFor(() => {
|
|
673
|
+
expect(result.current.firstInited).toBe(true);
|
|
674
|
+
expect(result.current.secondInited).toBe(true);
|
|
675
|
+
});
|
|
676
|
+
expect(firstInit).toHaveBeenCalledTimes(1);
|
|
677
|
+
expect(secondInit).toHaveBeenCalledTimes(1);
|
|
678
|
+
});
|
|
679
|
+
it('在 init 返回非布尔值时应该抛出错误', async () => {
|
|
680
|
+
var _a;
|
|
681
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
682
|
+
let InvalidReturnService = (() => {
|
|
683
|
+
var _a, _InvalidReturnService_inited_accessor_storage;
|
|
684
|
+
let _classSuper = Service;
|
|
685
|
+
let _inited_decorators;
|
|
686
|
+
let _inited_initializers = [];
|
|
687
|
+
let _inited_extraInitializers = [];
|
|
688
|
+
return _a = class InvalidReturnService extends _classSuper {
|
|
689
|
+
get inited() { return __classPrivateFieldGet(this, _InvalidReturnService_inited_accessor_storage, "f"); }
|
|
690
|
+
set inited(value) { __classPrivateFieldSet(this, _InvalidReturnService_inited_accessor_storage, value, "f"); }
|
|
691
|
+
constructor() {
|
|
692
|
+
super();
|
|
693
|
+
_InvalidReturnService_inited_accessor_storage.set(this, __runInitializers(this, _inited_initializers, false));
|
|
694
|
+
__runInitializers(this, _inited_extraInitializers);
|
|
695
|
+
makeObservable(this);
|
|
696
|
+
}
|
|
697
|
+
async init() {
|
|
698
|
+
return 'invalid';
|
|
699
|
+
}
|
|
700
|
+
},
|
|
701
|
+
_InvalidReturnService_inited_accessor_storage = new WeakMap(),
|
|
702
|
+
(() => {
|
|
703
|
+
var _b;
|
|
704
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_b = _classSuper[Symbol.metadata]) !== null && _b !== void 0 ? _b : null) : void 0;
|
|
705
|
+
_inited_decorators = [observable];
|
|
706
|
+
__esDecorate(_a, null, _inited_decorators, { kind: "accessor", name: "inited", static: false, private: false, access: { has: obj => "inited" in obj, get: obj => obj.inited, set: (obj, value) => { obj.inited = value; } }, metadata: _metadata }, _inited_initializers, _inited_extraInitializers);
|
|
707
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
708
|
+
})(),
|
|
709
|
+
_a;
|
|
710
|
+
})();
|
|
711
|
+
const TestProvider = createTestProvider([InvalidReturnService]);
|
|
712
|
+
const capturedErrors = [];
|
|
713
|
+
const Wrapper = ({ children }) => (_jsx(TestErrorBoundary, { onError: error => capturedErrors.push(error), children: _jsx(TestProvider, { children: children }) }));
|
|
714
|
+
try {
|
|
715
|
+
renderHook(() => useService(InvalidReturnService, service => service.inited), { wrapper: Wrapper });
|
|
716
|
+
await waitFor(() => {
|
|
717
|
+
expect(capturedErrors[0]).toBeInstanceOf(Error);
|
|
718
|
+
});
|
|
719
|
+
expect((_a = capturedErrors[0]) === null || _a === void 0 ? void 0 : _a.message).toBe('Service init must return a boolean value.');
|
|
720
|
+
}
|
|
721
|
+
finally {
|
|
722
|
+
errorSpy.mockRestore();
|
|
723
|
+
}
|
|
724
|
+
});
|
|
449
725
|
});
|
|
450
726
|
describe('边界情况', () => {
|
|
451
727
|
it('useService 应该能够处理选择器返回 undefined 的情况', () => {
|
|
@@ -630,6 +906,52 @@ describe('service integration', () => {
|
|
|
630
906
|
expect(result.current).toBe(false);
|
|
631
907
|
});
|
|
632
908
|
});
|
|
909
|
+
it('在 init 返回非布尔值时应该抛出错误', async () => {
|
|
910
|
+
var _a;
|
|
911
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
912
|
+
let InvalidReturnLocalService = (() => {
|
|
913
|
+
var _a, _InvalidReturnLocalService_inited_accessor_storage;
|
|
914
|
+
let _classSuper = Service;
|
|
915
|
+
let _inited_decorators;
|
|
916
|
+
let _inited_initializers = [];
|
|
917
|
+
let _inited_extraInitializers = [];
|
|
918
|
+
return _a = class InvalidReturnLocalService extends _classSuper {
|
|
919
|
+
get inited() { return __classPrivateFieldGet(this, _InvalidReturnLocalService_inited_accessor_storage, "f"); }
|
|
920
|
+
set inited(value) { __classPrivateFieldSet(this, _InvalidReturnLocalService_inited_accessor_storage, value, "f"); }
|
|
921
|
+
constructor() {
|
|
922
|
+
super();
|
|
923
|
+
_InvalidReturnLocalService_inited_accessor_storage.set(this, __runInitializers(this, _inited_initializers, false));
|
|
924
|
+
__runInitializers(this, _inited_extraInitializers);
|
|
925
|
+
makeObservable(this);
|
|
926
|
+
}
|
|
927
|
+
async init() {
|
|
928
|
+
return 'invalid';
|
|
929
|
+
}
|
|
930
|
+
},
|
|
931
|
+
_InvalidReturnLocalService_inited_accessor_storage = new WeakMap(),
|
|
932
|
+
(() => {
|
|
933
|
+
var _b;
|
|
934
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_b = _classSuper[Symbol.metadata]) !== null && _b !== void 0 ? _b : null) : void 0;
|
|
935
|
+
_inited_decorators = [observable];
|
|
936
|
+
__esDecorate(_a, null, _inited_decorators, { kind: "accessor", name: "inited", static: false, private: false, access: { has: obj => "inited" in obj, get: obj => obj.inited, set: (obj, value) => { obj.inited = value; } }, metadata: _metadata }, _inited_initializers, _inited_extraInitializers);
|
|
937
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
938
|
+
})(),
|
|
939
|
+
_a;
|
|
940
|
+
})();
|
|
941
|
+
const TestProvider = createTestProvider([TestService]);
|
|
942
|
+
const capturedErrors = [];
|
|
943
|
+
const Wrapper = ({ children }) => (_jsx(TestErrorBoundary, { onError: error => capturedErrors.push(error), children: _jsx(TestProvider, { children: children }) }));
|
|
944
|
+
try {
|
|
945
|
+
renderHook(() => useLocalService(InvalidReturnLocalService, service => service.inited), { wrapper: Wrapper });
|
|
946
|
+
await waitFor(() => {
|
|
947
|
+
expect(capturedErrors[0]).toBeInstanceOf(Error);
|
|
948
|
+
});
|
|
949
|
+
expect((_a = capturedErrors[0]) === null || _a === void 0 ? void 0 : _a.message).toBe('Service init must return a boolean value.');
|
|
950
|
+
}
|
|
951
|
+
finally {
|
|
952
|
+
errorSpy.mockRestore();
|
|
953
|
+
}
|
|
954
|
+
});
|
|
633
955
|
it('应该能够处理没有实现 Service 接口的局部服务', async () => {
|
|
634
956
|
const TestProvider = createTestProvider([TestService]);
|
|
635
957
|
const { result } = renderHook(() => useLocalService(PlainClass, service => ({
|
|
@@ -756,6 +1078,198 @@ describe('service integration', () => {
|
|
|
756
1078
|
expect(result2.current.localService.counter).toBe(2);
|
|
757
1079
|
expect(result2.current.localService.data).toBe('local2-changed');
|
|
758
1080
|
});
|
|
1081
|
+
it('当 selector 返回服务实例时应该退化为监听全部属性', async () => {
|
|
1082
|
+
const TestProvider = createTestProvider([TestService]);
|
|
1083
|
+
const { result } = renderHook(() => useLocalService(LocalService, service => service), { wrapper: TestProvider });
|
|
1084
|
+
const firstInstance = result.current;
|
|
1085
|
+
expect(firstInstance.counter).toBe(0);
|
|
1086
|
+
act(() => {
|
|
1087
|
+
firstInstance.increment();
|
|
1088
|
+
});
|
|
1089
|
+
await waitFor(() => {
|
|
1090
|
+
expect(result.current.counter).toBe(1);
|
|
1091
|
+
});
|
|
1092
|
+
expect(result.current).toBe(firstInstance);
|
|
1093
|
+
});
|
|
1094
|
+
it('应该在卸载后重新创建新的服务实例', async () => {
|
|
1095
|
+
const TestProvider = createTestProvider([TestService]);
|
|
1096
|
+
const { result, unmount } = renderHook(() => useLocalService(LocalService, service => service), { wrapper: TestProvider });
|
|
1097
|
+
const firstInstance = result.current;
|
|
1098
|
+
await waitFor(() => {
|
|
1099
|
+
expect(firstInstance.inited).toBe(true);
|
|
1100
|
+
});
|
|
1101
|
+
unmount();
|
|
1102
|
+
const { result: result2 } = renderHook(() => useLocalService(LocalService, service => service), { wrapper: TestProvider });
|
|
1103
|
+
await waitFor(() => {
|
|
1104
|
+
expect(result2.current.inited).toBe(true);
|
|
1105
|
+
});
|
|
1106
|
+
expect(result2.current).not.toBe(firstInstance);
|
|
1107
|
+
});
|
|
1108
|
+
it('应该只在首次挂载时调用一次 init', async () => {
|
|
1109
|
+
const initSpy = vi.fn(async () => true);
|
|
1110
|
+
let InitOnceLocalService = (() => {
|
|
1111
|
+
var _a, _InitOnceLocalService_inited_accessor_storage;
|
|
1112
|
+
let _classSuper = Service;
|
|
1113
|
+
let _inited_decorators;
|
|
1114
|
+
let _inited_initializers = [];
|
|
1115
|
+
let _inited_extraInitializers = [];
|
|
1116
|
+
return _a = class InitOnceLocalService extends _classSuper {
|
|
1117
|
+
get inited() { return __classPrivateFieldGet(this, _InitOnceLocalService_inited_accessor_storage, "f"); }
|
|
1118
|
+
set inited(value) { __classPrivateFieldSet(this, _InitOnceLocalService_inited_accessor_storage, value, "f"); }
|
|
1119
|
+
constructor() {
|
|
1120
|
+
super();
|
|
1121
|
+
_InitOnceLocalService_inited_accessor_storage.set(this, __runInitializers(this, _inited_initializers, false));
|
|
1122
|
+
this.init = (__runInitializers(this, _inited_extraInitializers), initSpy);
|
|
1123
|
+
makeObservable(this);
|
|
1124
|
+
}
|
|
1125
|
+
},
|
|
1126
|
+
_InitOnceLocalService_inited_accessor_storage = new WeakMap(),
|
|
1127
|
+
(() => {
|
|
1128
|
+
var _b;
|
|
1129
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_b = _classSuper[Symbol.metadata]) !== null && _b !== void 0 ? _b : null) : void 0;
|
|
1130
|
+
_inited_decorators = [observable];
|
|
1131
|
+
__esDecorate(_a, null, _inited_decorators, { kind: "accessor", name: "inited", static: false, private: false, access: { has: obj => "inited" in obj, get: obj => obj.inited, set: (obj, value) => { obj.inited = value; } }, metadata: _metadata }, _inited_initializers, _inited_extraInitializers);
|
|
1132
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
1133
|
+
})(),
|
|
1134
|
+
_a;
|
|
1135
|
+
})();
|
|
1136
|
+
const TestProvider = createTestProvider([TestService]);
|
|
1137
|
+
const { result, rerender } = renderHook(() => useLocalService(InitOnceLocalService, service => service.inited), { wrapper: TestProvider });
|
|
1138
|
+
await waitFor(() => {
|
|
1139
|
+
expect(result.current).toBe(true);
|
|
1140
|
+
});
|
|
1141
|
+
rerender();
|
|
1142
|
+
await waitFor(() => {
|
|
1143
|
+
expect(result.current).toBe(true);
|
|
1144
|
+
});
|
|
1145
|
+
expect(initSpy).toHaveBeenCalledTimes(1);
|
|
1146
|
+
});
|
|
1147
|
+
it('在同一组件中混用全局与局部的同类服务时应该保持状态独立', async () => {
|
|
1148
|
+
const TestProvider = createTestProvider([TestService]);
|
|
1149
|
+
const { result } = renderHook(() => ({
|
|
1150
|
+
global: useService(TestService, service => ({
|
|
1151
|
+
service,
|
|
1152
|
+
count: service.count,
|
|
1153
|
+
users: [...service.users]
|
|
1154
|
+
})),
|
|
1155
|
+
local: useLocalService(TestService, service => ({
|
|
1156
|
+
service,
|
|
1157
|
+
count: service.count,
|
|
1158
|
+
users: [...service.users]
|
|
1159
|
+
}))
|
|
1160
|
+
}), { wrapper: TestProvider });
|
|
1161
|
+
await waitFor(() => {
|
|
1162
|
+
expect(result.current.global.users).toEqual(['Alice', 'Bob']);
|
|
1163
|
+
expect(result.current.local.users).toEqual(['Alice', 'Bob']);
|
|
1164
|
+
});
|
|
1165
|
+
expect(result.current.global.service).not.toBe(result.current.local.service);
|
|
1166
|
+
act(() => {
|
|
1167
|
+
result.current.global.service.increment();
|
|
1168
|
+
result.current.global.service.addUser('GlobalCharlie');
|
|
1169
|
+
});
|
|
1170
|
+
await waitFor(() => {
|
|
1171
|
+
expect(result.current.global.count).toBe(1);
|
|
1172
|
+
expect(result.current.global.users).toEqual(['Alice', 'Bob', 'GlobalCharlie']);
|
|
1173
|
+
});
|
|
1174
|
+
expect(result.current.local.count).toBe(0);
|
|
1175
|
+
expect(result.current.local.users).toEqual(['Alice', 'Bob']);
|
|
1176
|
+
act(() => {
|
|
1177
|
+
result.current.local.service.increment();
|
|
1178
|
+
result.current.local.service.addUser('LocalCharlie');
|
|
1179
|
+
});
|
|
1180
|
+
await waitFor(() => {
|
|
1181
|
+
expect(result.current.local.count).toBe(1);
|
|
1182
|
+
expect(result.current.local.users).toEqual(['Alice', 'Bob', 'LocalCharlie']);
|
|
1183
|
+
});
|
|
1184
|
+
expect(result.current.global.count).toBe(1);
|
|
1185
|
+
expect(result.current.global.users).toEqual(['Alice', 'Bob', 'GlobalCharlie']);
|
|
1186
|
+
});
|
|
1187
|
+
it('同一服务在全局与局部同时使用时应该分别初始化', async () => {
|
|
1188
|
+
const initSpy = vi.fn(async function () {
|
|
1189
|
+
runInAction(() => {
|
|
1190
|
+
this.inited = true;
|
|
1191
|
+
});
|
|
1192
|
+
return true;
|
|
1193
|
+
});
|
|
1194
|
+
let InitSpyService = (() => {
|
|
1195
|
+
var _a, _InitSpyService_inited_accessor_storage;
|
|
1196
|
+
let _classSuper = Service;
|
|
1197
|
+
let _inited_decorators;
|
|
1198
|
+
let _inited_initializers = [];
|
|
1199
|
+
let _inited_extraInitializers = [];
|
|
1200
|
+
return _a = class InitSpyService extends _classSuper {
|
|
1201
|
+
get inited() { return __classPrivateFieldGet(this, _InitSpyService_inited_accessor_storage, "f"); }
|
|
1202
|
+
set inited(value) { __classPrivateFieldSet(this, _InitSpyService_inited_accessor_storage, value, "f"); }
|
|
1203
|
+
constructor() {
|
|
1204
|
+
super();
|
|
1205
|
+
_InitSpyService_inited_accessor_storage.set(this, __runInitializers(this, _inited_initializers, false));
|
|
1206
|
+
this.init = (__runInitializers(this, _inited_extraInitializers), initSpy);
|
|
1207
|
+
makeObservable(this);
|
|
1208
|
+
}
|
|
1209
|
+
},
|
|
1210
|
+
_InitSpyService_inited_accessor_storage = new WeakMap(),
|
|
1211
|
+
(() => {
|
|
1212
|
+
var _b;
|
|
1213
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_b = _classSuper[Symbol.metadata]) !== null && _b !== void 0 ? _b : null) : void 0;
|
|
1214
|
+
_inited_decorators = [observable];
|
|
1215
|
+
__esDecorate(_a, null, _inited_decorators, { kind: "accessor", name: "inited", static: false, private: false, access: { has: obj => "inited" in obj, get: obj => obj.inited, set: (obj, value) => { obj.inited = value; } }, metadata: _metadata }, _inited_initializers, _inited_extraInitializers);
|
|
1216
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
1217
|
+
})(),
|
|
1218
|
+
_a;
|
|
1219
|
+
})();
|
|
1220
|
+
const TestProvider = createTestProvider([InitSpyService]);
|
|
1221
|
+
const { result } = renderHook(() => ({
|
|
1222
|
+
global: useService(InitSpyService, service => ({
|
|
1223
|
+
service,
|
|
1224
|
+
inited: service.inited
|
|
1225
|
+
})),
|
|
1226
|
+
local: useLocalService(InitSpyService, service => ({
|
|
1227
|
+
service,
|
|
1228
|
+
inited: service.inited
|
|
1229
|
+
}))
|
|
1230
|
+
}), { wrapper: TestProvider });
|
|
1231
|
+
await waitFor(() => {
|
|
1232
|
+
expect(result.current.global.inited).toBe(true);
|
|
1233
|
+
expect(result.current.local.inited).toBe(true);
|
|
1234
|
+
});
|
|
1235
|
+
expect(result.current.global.service).not.toBe(result.current.local.service);
|
|
1236
|
+
expect(initSpy).toHaveBeenCalledTimes(2);
|
|
1237
|
+
const instances = initSpy.mock.instances;
|
|
1238
|
+
expect(new Set(instances).size).toBe(2);
|
|
1239
|
+
instances.forEach(instance => {
|
|
1240
|
+
expect(instance.inited).toBe(true);
|
|
1241
|
+
});
|
|
1242
|
+
});
|
|
1243
|
+
it('useLocalService 的 selector 应该能够响应全局服务的 observable 变化', async () => {
|
|
1244
|
+
const TestProvider = createTestProvider([TestService]);
|
|
1245
|
+
const { result } = renderHook(() => {
|
|
1246
|
+
const globalService = useService(TestService, service => service);
|
|
1247
|
+
const localSelection = useLocalService(LocalService, service => ({
|
|
1248
|
+
service,
|
|
1249
|
+
combined: `${service.counter}-${globalService.count}`
|
|
1250
|
+
}));
|
|
1251
|
+
return {
|
|
1252
|
+
globalService,
|
|
1253
|
+
localSelection
|
|
1254
|
+
};
|
|
1255
|
+
}, { wrapper: TestProvider });
|
|
1256
|
+
await waitFor(() => {
|
|
1257
|
+
expect(result.current.localSelection.service.inited).toBe(true);
|
|
1258
|
+
});
|
|
1259
|
+
expect(result.current.localSelection.combined).toBe('0-0');
|
|
1260
|
+
act(() => {
|
|
1261
|
+
result.current.globalService.increment();
|
|
1262
|
+
});
|
|
1263
|
+
await waitFor(() => {
|
|
1264
|
+
expect(result.current.localSelection.combined).toBe('0-1');
|
|
1265
|
+
});
|
|
1266
|
+
act(() => {
|
|
1267
|
+
result.current.localSelection.service.increment();
|
|
1268
|
+
});
|
|
1269
|
+
await waitFor(() => {
|
|
1270
|
+
expect(result.current.localSelection.combined).toBe('1-1');
|
|
1271
|
+
});
|
|
1272
|
+
});
|
|
759
1273
|
});
|
|
760
1274
|
describe('useLocalService 可选 selector 功能', () => {
|
|
761
1275
|
it('应该在不传入 selector 时返回完整的服务实例', async () => {
|
|
@@ -831,5 +1345,55 @@ describe('service integration', () => {
|
|
|
831
1345
|
expect(result.current.data).toBe('changed');
|
|
832
1346
|
});
|
|
833
1347
|
});
|
|
1348
|
+
it('应该正确处理同步 init 的局部服务', async () => {
|
|
1349
|
+
const TestProvider = createTestProvider([TestService]);
|
|
1350
|
+
let SyncInitLocalService = (() => {
|
|
1351
|
+
var _a, _SyncInitLocalService_inited_accessor_storage, _SyncInitLocalService_value_accessor_storage;
|
|
1352
|
+
let _inited_decorators;
|
|
1353
|
+
let _inited_initializers = [];
|
|
1354
|
+
let _inited_extraInitializers = [];
|
|
1355
|
+
let _value_decorators;
|
|
1356
|
+
let _value_initializers = [];
|
|
1357
|
+
let _value_extraInitializers = [];
|
|
1358
|
+
return _a = class SyncInitLocalService {
|
|
1359
|
+
get inited() { return __classPrivateFieldGet(this, _SyncInitLocalService_inited_accessor_storage, "f"); }
|
|
1360
|
+
set inited(value) { __classPrivateFieldSet(this, _SyncInitLocalService_inited_accessor_storage, value, "f"); }
|
|
1361
|
+
get value() { return __classPrivateFieldGet(this, _SyncInitLocalService_value_accessor_storage, "f"); }
|
|
1362
|
+
set value(value) { __classPrivateFieldSet(this, _SyncInitLocalService_value_accessor_storage, value, "f"); }
|
|
1363
|
+
constructor() {
|
|
1364
|
+
_SyncInitLocalService_inited_accessor_storage.set(this, __runInitializers(this, _inited_initializers, false));
|
|
1365
|
+
_SyncInitLocalService_value_accessor_storage.set(this, (__runInitializers(this, _inited_extraInitializers), __runInitializers(this, _value_initializers, 'pending')));
|
|
1366
|
+
__runInitializers(this, _value_extraInitializers);
|
|
1367
|
+
makeObservable(this);
|
|
1368
|
+
}
|
|
1369
|
+
init() {
|
|
1370
|
+
runInAction(() => {
|
|
1371
|
+
this.value = 'ready';
|
|
1372
|
+
this.inited = true;
|
|
1373
|
+
});
|
|
1374
|
+
return true;
|
|
1375
|
+
}
|
|
1376
|
+
},
|
|
1377
|
+
_SyncInitLocalService_inited_accessor_storage = new WeakMap(),
|
|
1378
|
+
_SyncInitLocalService_value_accessor_storage = new WeakMap(),
|
|
1379
|
+
(() => {
|
|
1380
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
1381
|
+
_inited_decorators = [observable];
|
|
1382
|
+
_value_decorators = [observable];
|
|
1383
|
+
__esDecorate(_a, null, _inited_decorators, { kind: "accessor", name: "inited", static: false, private: false, access: { has: obj => "inited" in obj, get: obj => obj.inited, set: (obj, value) => { obj.inited = value; } }, metadata: _metadata }, _inited_initializers, _inited_extraInitializers);
|
|
1384
|
+
__esDecorate(_a, null, _value_decorators, { kind: "accessor", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
|
|
1385
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
1386
|
+
})(),
|
|
1387
|
+
_a;
|
|
1388
|
+
})();
|
|
1389
|
+
const { result } = renderHook(() => useLocalService(SyncInitLocalService, service => ({
|
|
1390
|
+
inited: service.inited,
|
|
1391
|
+
value: service.value
|
|
1392
|
+
})), { wrapper: TestProvider });
|
|
1393
|
+
await waitFor(() => {
|
|
1394
|
+
expect(result.current.inited).toBe(true);
|
|
1395
|
+
expect(result.current.value).toBe('ready');
|
|
1396
|
+
});
|
|
1397
|
+
});
|
|
834
1398
|
});
|
|
835
1399
|
});
|