@blac/react 2.0.20 → 2.0.21

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.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createContext, useContext, useEffect, useId, useLayoutEffect, useMemo, useReducer, useRef } from "react";
2
- import { DEP_BRAND, getRegistry, resolveInstanceKey } from "@blac/core";
1
+ import { createContext, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useSyncExternalStore } from "react";
2
+ import { APPLY_DEPS, DEP_BRAND, REMOVE_DEPS_OWNER, WITH_TRACKED_STATE, getRegistry, resolveInstanceKey } from "@blac/core";
3
3
  import { ALL_PATHS, ProxyCache, emptyPathSet, pathSetEquals, trackRender, untracked } from "@dirtytalk/structural";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region src/BlocProvider.tsx
@@ -29,6 +29,7 @@ const ProvidedArgsContext = createContext(/* @__PURE__ */ new Map());
29
29
  * <CartWidget />
30
30
  * </BlocProvider>
31
31
  * ```
32
+ * @public
32
33
  */
33
34
  function BlocProvider({ bloc, args, children }) {
34
35
  const parentMap = useContext(ProvidedArgsContext);
@@ -39,7 +40,7 @@ function BlocProvider({ bloc, args, children }) {
39
40
  }, [
40
41
  parentMap,
41
42
  bloc,
42
- args
43
+ resolveInstanceKey(bloc, args)
43
44
  ]);
44
45
  return /* @__PURE__ */ jsx(ProvidedArgsContext.Provider, {
45
46
  value: mergedMap,
@@ -51,36 +52,60 @@ function BlocProvider({ bloc, args, children }) {
51
52
  * bloc class, or `undefined` when called outside a matching provider.
52
53
  *
53
54
  * Used by `useBloc` to inherit provider args when no own `args` are given.
55
+ * @public
54
56
  */
55
57
  function useProvidedArgs(BlocClass) {
56
58
  return useContext(ProvidedArgsContext).get(BlocClass);
57
59
  }
58
60
  //#endregion
61
+ //#region src/RegistryProvider.tsx
62
+ /**
63
+ * Internal context value: a registry to use in place of the module-global
64
+ * default (`getRegistry()`), or `null` when no provider is present.
65
+ */
66
+ const RegistryContext = createContext(null);
67
+ /**
68
+ * Scopes descendant `useBloc` calls to a specific registry instead of the
69
+ * module-global default.
70
+ *
71
+ * Useful for test isolation, SSR isolation, and micro-frontends that must
72
+ * not share bloc instances with the host page.
73
+ *
74
+ * @example
75
+ * ```tsx
76
+ * <RegistryProvider registry={new StateContainerRegistry()}>
77
+ * <App />
78
+ * </RegistryProvider>
79
+ * ```
80
+ * @public
81
+ */
82
+ function RegistryProvider({ registry, children }) {
83
+ return /* @__PURE__ */ jsx(RegistryContext.Provider, {
84
+ value: registry,
85
+ children
86
+ });
87
+ }
88
+ //#endregion
59
89
  //#region src/buildTrackedProxy.ts
90
+ function supportsTrackedState(value) {
91
+ return typeof value[WITH_TRACKED_STATE] === "function";
92
+ }
60
93
  /**
61
- * Build a per-consumer proxy pair for a bloc instance.
94
+ * Build a per-consumer proxy for a bloc instance.
62
95
  *
63
- * Returns two proxies:
64
- * - `proxy` — the stable outer proxy returned to the consumer. Getter
65
- * properties are invoked with `thisProxy` as `this` so that `this.state`
66
- * reads inside getters are redirected to the current render's tracking
67
- * proxy. Non-getter properties fall through to the live instance.
68
- * - `thisProxy` — the inner `this`-proxy used as the receiver for getter
69
- * calls. Intercepts `state` to return `trackedStateRef.current` when a
70
- * tracking context is active; otherwise falls through to the live value.
71
- * When `onDepHandle` is supplied, a read off `this` whose value carries the
72
- * `DEP_BRAND` symbol (i.e. a `depend()` handle) is routed through
73
- * `onDepHandle`, which returns a per-consumer wrapper whose `.track()` opts
74
- * the consumer into cross-bloc reactivity. Core stays decoupled — detection
75
- * is purely by the `DEP_BRAND` symbol.
96
+ * Getters run with `this` bound to the **real instance**, not a proxy, so ES
97
+ * `#private` fields and methods work in user blocs. Path recording is done by
98
+ * `[WITH_TRACKED_STATE]`, which makes the instance's own `state` getter report
99
+ * the render's tracking proxy for the duration of the call; nested getter
100
+ * chains stay tracked because the override is still set.
76
101
  *
77
- * Both allocations happen exactly once per bloc acquisition (inside `useMemo`)
78
- * so the proxies are stable across renders.
102
+ * One allocation per bloc acquisition (inside `useMemo`), so the proxy is
103
+ * stable across renders.
79
104
  *
80
- * @param onDepHandle - Optional callback invoked when a getter reads a branded
81
- * dep handle off `this`. Receives the original handle and returns the value
82
- * to expose in its place (the session-bound wrapper). The callback is
83
- * responsible for caching wrappers per handle to avoid re-allocation.
105
+ * @param onDepHandle - Optional callback invoked when a read returns a branded
106
+ * dep handle. Receives the original handle and returns the value to expose in
107
+ * its place (the session-bound wrapper). The callback is responsible for
108
+ * caching wrappers per handle to avoid re-allocation.
84
109
  */
85
110
  function buildTrackedProxy(instance, trackedStateRef, onDepHandle) {
86
111
  const getterDescs = /* @__PURE__ */ new Map();
@@ -93,20 +118,29 @@ function buildTrackedProxy(instance, trackedStateRef, onDepHandle) {
93
118
  }
94
119
  proto = Object.getPrototypeOf(proto);
95
120
  }
96
- const thisProxy = new Proxy(instance, { get(t, k, r) {
97
- if (k === "state") return trackedStateRef.current ?? Reflect.get(t, k, r);
98
- const value = Reflect.get(t, k, r);
121
+ const boundMethods = /* @__PURE__ */ new Map();
122
+ const wrapDepHandle = (value) => {
99
123
  if (onDepHandle !== void 0 && (typeof value === "function" || typeof value === "object") && value !== null && value[DEP_BRAND] !== void 0) return onDepHandle(value);
100
124
  return value;
101
- } });
102
- return {
103
- proxy: new Proxy(instance, { get(target, key, receiver) {
104
- const desc = getterDescs.get(key);
105
- if (desc?.get) return desc.get.call(thisProxy);
106
- return Reflect.get(target, key, receiver);
107
- } }),
108
- thisProxy
109
125
  };
126
+ return { proxy: new Proxy(instance, { get(target, key) {
127
+ const getter = getterDescs.get(key)?.get;
128
+ if (getter) {
129
+ const tracked = trackedStateRef.current;
130
+ const read = () => getter.call(target);
131
+ return wrapDepHandle(tracked == null || !supportsTrackedState(target) ? read() : target[WITH_TRACKED_STATE](tracked, read, onDepHandle));
132
+ }
133
+ const value = Reflect.get(target, key, target);
134
+ if (typeof value === "function") {
135
+ let bound = boundMethods.get(key);
136
+ if (bound === void 0) {
137
+ bound = value.bind(target);
138
+ boundMethods.set(key, bound);
139
+ }
140
+ return wrapDepHandle(bound);
141
+ }
142
+ return wrapDepHandle(value);
143
+ } }) };
110
144
  }
111
145
  //#endregion
112
146
  //#region src/useBloc.ts
@@ -122,7 +156,7 @@ const depRefId = (consumerId) => `useBloc@${consumerId}:dep`;
122
156
  * - **Auto-tracking** (default): the returned state value is a proxy that
123
157
  * records read paths during render. The component re-renders when any
124
158
  * recorded path changes. Backed by `@dirtytalk/structural`'s
125
- * {@link trackRender} + the container's path-scoped `DirtyChannel`.
159
+ * `trackRender` + the container's path-scoped `DirtyChannel`.
126
160
  * - **Manual select**: pass `options.select` to opt out of auto-tracking.
127
161
  * The hook re-renders only when the returned array's elements change
128
162
  * (per-index `Object.is`).
@@ -142,7 +176,7 @@ const depRefId = (consumerId) => `useBloc@${consumerId}:dep`;
142
176
  * const [state, bloc] = useBloc(MyBloc, { args: { _id: id } });
143
177
  * ```
144
178
  *
145
- * @template T - The state container constructor type (inferred from BlocClass)
179
+ * @typeParam T - The state container constructor type (inferred from BlocClass)
146
180
  * @param BlocClass - The state container class to connect to
147
181
  * @param options - Configuration options
148
182
  * @returns Tuple of `[state, bloc, ref]`
@@ -163,13 +197,13 @@ const depRefId = (consumerId) => `useBloc@${consumerId}:dep`;
163
197
  * ```ts
164
198
  * const [state, bloc] = useBloc(UserBloc, { args: { userId: 'alice' } });
165
199
  * ```
200
+ * @public
166
201
  */
167
202
  function useBloc(BlocClass, options) {
168
- const componentRef = useRef({});
203
+ const registry = useContext(RegistryContext) ?? getRegistry();
169
204
  const consumerIdRef = useRef(null);
170
205
  if (consumerIdRef.current === null) consumerIdRef.current = `useBloc-${nextConsumerId++}`;
171
206
  const consumerId = consumerIdRef.current;
172
- useId();
173
207
  const selectRef = useRef(options?.select);
174
208
  selectRef.current = options?.select;
175
209
  const onMountRef = useRef(options?.onMount);
@@ -200,27 +234,30 @@ function useBloc(BlocClass, options) {
200
234
  key: providerArgs === void 0 ? void 0 : JSON.stringify(providerArgs)
201
235
  };
202
236
  const providerArgsKey = providerArgsKeyRef.current.key;
237
+ const resolveEffectiveArgs = () => ownArgsRef.current !== void 0 ? ownArgsRef.current : providerArgsRef.current;
203
238
  const trackedStateRef = useRef(null);
204
- const sessionRef = useRef(/* @__PURE__ */ new Map());
205
- const depSubsRef = useRef(/* @__PURE__ */ new Map());
206
- const depWrapperCacheRef = useRef(/* @__PURE__ */ new Map());
207
239
  const proxyCacheRef = useRef(new ProxyCache());
208
- const lastReconcileRef = useRef(null);
209
- const [rebindNonce, bumpRebind] = useReducer((x) => x + 1, 0);
240
+ const depWrapperCacheRef = useRef(/* @__PURE__ */ new Map());
241
+ const rebindNonceRef = useRef(0);
242
+ const rebindNonce = rebindNonceRef.current;
243
+ const consumerRef = useRef(null);
244
+ consumerRef.current ??= createConsumer();
245
+ const consumer = consumerRef.current;
210
246
  const ownedBlocRef = useRef(null);
211
247
  const { bloc, instanceKey, trackedBloc } = useMemo(() => {
212
- const effectiveArgs = ownArgsRef.current !== void 0 ? ownArgsRef.current : providerArgsRef.current;
248
+ const effectiveArgs = resolveEffectiveArgs();
213
249
  const resolvedKey = resolveInstanceKey(BlocClass, effectiveArgs);
214
- const instance = getRegistry().acquire(BlocClass, resolvedKey, {
250
+ const instance = registry.acquire(BlocClass, resolvedKey, {
215
251
  canCreate: true,
216
252
  countRef: false,
217
- args: effectiveArgs
253
+ args: effectiveArgs,
254
+ sweepIfUnowned: true
218
255
  });
219
256
  const onDepHandle = (handle) => {
220
257
  const cache = depWrapperCacheRef.current;
221
258
  const cached = cache.get(handle);
222
259
  if (cached !== void 0) return cached;
223
- const wrapper = makeDepWrapper(handle, consumerId, trackedStateRef, sessionRef, onDepHandle);
260
+ const wrapper = makeDepWrapper(handle, consumerId, registry, trackedStateRef, consumer, onDepHandle);
224
261
  cache.set(handle, wrapper);
225
262
  return wrapper;
226
263
  };
@@ -234,58 +271,50 @@ function useBloc(BlocClass, options) {
234
271
  BlocClass,
235
272
  ownArgsKey,
236
273
  providerArgsKey,
237
- rebindNonce
274
+ rebindNonce,
275
+ registry
238
276
  ]);
239
- const [, force] = useReducer((x) => x + 1, 0);
240
- const pathRef = useRef(emptyPathSet());
241
- const expandedInterestRef = useRef(emptyPathSet());
242
- const lastSelectionRef = useRef(null);
243
- const renderStateRef = useRef(void 0);
244
- const prevBlocRef = useRef(null);
245
- useEffect(() => {
246
- const channel = bloc.channel;
247
- if (selectRef.current !== void 0) {
248
- const unsub = channel.subscribe(() => ALL_PATHS, () => {
277
+ consumer.container = bloc;
278
+ useSyncExternalStore(useMemo(() => (onStoreChange) => {
279
+ consumer.notify = onStoreChange;
280
+ const container = consumer.container;
281
+ const unsubscribe = container.channel.subscribe(() => consumer.interest, () => {
282
+ if (consumer.isSelectMode) {
249
283
  const select = selectRef.current;
250
- if (!select) {
251
- force();
252
- return;
253
- }
254
- const next = select(bloc.state, bloc);
255
- const prev = lastSelectionRef.current;
256
- if (prev !== null && shallowArrayEqual(prev, next)) return;
257
- lastSelectionRef.current = next;
258
- force();
259
- });
260
- const select = selectRef.current;
261
- if (select) {
262
- const next = select(bloc.state, bloc);
263
- const prev = lastSelectionRef.current;
264
- if (prev === null || !shallowArrayEqual(prev, next)) {
265
- lastSelectionRef.current = next;
266
- force();
284
+ if (select) {
285
+ const next = select(container.state, container);
286
+ const prev = consumer.selection;
287
+ if (prev !== null && shallowArrayEqual(prev, next)) return;
288
+ consumer.selection = next;
267
289
  }
268
290
  }
269
- return unsub;
270
- }
271
- const unsub = channel.subscribe(() => expandedInterestRef.current, () => force());
272
- bloc.registerConsumerPaths(consumerId, pathRef.current);
273
- if (bloc.state !== renderStateRef.current) force();
291
+ consumer.bump();
292
+ });
293
+ container.registerConsumerPaths(consumerId, consumer.paths);
274
294
  return () => {
275
- unsub();
276
- bloc.unregisterConsumer(consumerId);
295
+ consumer.notify = noop;
296
+ unsubscribe();
297
+ container.unregisterConsumer(consumerId);
277
298
  };
278
- }, [bloc, consumerId]);
299
+ }, [
300
+ BlocClass,
301
+ instanceKey,
302
+ consumerId,
303
+ rebindNonce
304
+ ]), consumer.getSnapshot, consumer.getServerSnapshot);
279
305
  useLayoutEffect(() => {
280
- const registry = getRegistry();
281
306
  const live = registry.acquire(BlocClass, instanceKey, {
282
307
  canCreate: true,
283
308
  countRef: true,
284
- refId: primaryRefId(consumerId)
309
+ refId: primaryRefId(consumerId),
310
+ args: resolveEffectiveArgs()
285
311
  });
286
312
  ownedBlocRef.current = live;
287
313
  onMountRef.current?.(live);
288
- if (live !== bloc) bumpRebind();
314
+ if (live !== bloc) {
315
+ rebindNonceRef.current++;
316
+ consumer.bump();
317
+ }
289
318
  return () => {
290
319
  onUnmountRef.current?.(ownedBlocRef.current ?? bloc);
291
320
  registry.release(BlocClass, instanceKey, false, primaryRefId(consumerId));
@@ -293,26 +322,26 @@ function useBloc(BlocClass, options) {
293
322
  }, [
294
323
  BlocClass,
295
324
  instanceKey,
296
- consumerId
325
+ consumerId,
326
+ registry
297
327
  ]);
298
- const rawState = bloc.state;
299
- renderStateRef.current = rawState;
300
- if (prevBlocRef.current !== bloc) {
301
- prevBlocRef.current = bloc;
302
- lastSelectionRef.current = null;
303
- }
328
+ const container = consumer.container;
329
+ const rawState = container.state;
330
+ const select = selectRef.current;
331
+ consumer.isSelectMode = select !== void 0;
304
332
  let state;
305
- if (selectRef.current !== void 0) {
333
+ if (select !== void 0) {
306
334
  state = rawState;
307
- if (lastSelectionRef.current === null) lastSelectionRef.current = selectRef.current(rawState, bloc);
335
+ if (consumer.selection === null) consumer.selection = select(rawState, bloc);
336
+ consumer.interest = ALL_PATHS;
308
337
  } else {
309
- const tracked = trackRender(rawState, bloc.interner, proxyCacheRef.current);
338
+ const tracked = trackRender(rawState, container.interner, proxyCacheRef.current);
310
339
  state = tracked.value;
311
340
  trackedStateRef.current = tracked.value;
312
- pathRef.current = tracked.paths;
313
- const session = sessionRef.current;
341
+ consumer.paths = tracked.paths;
342
+ const session = consumer.session;
314
343
  session.clear();
315
- session.set(bloc, {
344
+ session.set(container, {
316
345
  kind: "primary",
317
346
  paths: tracked.paths
318
347
  });
@@ -320,15 +349,15 @@ function useBloc(BlocClass, options) {
320
349
  }
321
350
  useLayoutEffect(() => {
322
351
  trackedStateRef.current = null;
323
- if (selectRef.current !== void 0) {
324
- lastReconcileRef.current = null;
352
+ if (consumer.isSelectMode) {
353
+ consumer.lastReconcile = null;
325
354
  return;
326
355
  }
327
- const container = bloc;
328
- const paths = pathRef.current;
329
- const session = sessionRef.current;
330
- const last = lastReconcileRef.current;
331
- if (last !== null && last.primaryContainer === container && pathSetEquals(last.primaryPaths, paths)) {
356
+ const primary = consumer.container;
357
+ const paths = consumer.paths;
358
+ const session = consumer.session;
359
+ const last = consumer.lastReconcile;
360
+ if (last !== null && last.primaryContainer === primary && pathSetEquals(last.primaryPaths, paths)) {
332
361
  let unchanged = last.deps.size === session.size - 1;
333
362
  if (unchanged) for (const [depContainer, entry] of session) {
334
363
  if (entry.kind === "primary") continue;
@@ -340,13 +369,13 @@ function useBloc(BlocClass, options) {
340
369
  }
341
370
  if (unchanged) return;
342
371
  }
343
- container.registerConsumerPaths(consumerId, paths);
344
- expandedInterestRef.current = expandWithAncestors(paths, container.interner);
345
- const subs = depSubsRef.current;
372
+ primary.registerConsumerPaths(consumerId, paths);
373
+ consumer.interest = expandWithAncestors(paths, primary.interner);
374
+ const subs = consumer.depSubs;
346
375
  for (const [depContainer, sub] of subs) if (!session.has(depContainer)) {
347
376
  sub.unsubscribe();
348
377
  depContainer.unregisterConsumer(consumerId);
349
- getRegistry().release(sub.Type, sub.key, false, sub.refId);
378
+ registry.release(sub.Type, sub.key, false, sub.refId);
350
379
  subs.delete(depContainer);
351
380
  }
352
381
  for (const [depContainer, entry] of session) {
@@ -358,14 +387,14 @@ function useBloc(BlocClass, options) {
358
387
  existing.interestRef.current = interest;
359
388
  continue;
360
389
  }
361
- getRegistry().acquire(entry.Type, entry.key, {
390
+ registry.acquire(entry.Type, entry.key, {
362
391
  canCreate: true,
363
392
  countRef: true,
364
393
  refId: entry.refId,
365
394
  args: entry.args
366
395
  });
367
396
  const interestRef = { current: interest };
368
- const unsubscribe = depContainer.channel.subscribe(() => interestRef.current, () => force());
397
+ const unsubscribe = depContainer.channel.subscribe(() => interestRef.current, () => consumer.bump());
369
398
  subs.set(depContainer, {
370
399
  unsubscribe,
371
400
  interestRef,
@@ -385,28 +414,46 @@ function useBloc(BlocClass, options) {
385
414
  args: entry.args
386
415
  });
387
416
  }
388
- lastReconcileRef.current = {
389
- primaryContainer: container,
417
+ consumer.lastReconcile = {
418
+ primaryContainer: primary,
390
419
  primaryPaths: paths,
391
420
  deps: depsSignature
392
421
  };
393
422
  });
394
423
  useEffect(() => {
395
- const subs = depSubsRef.current;
424
+ const subs = consumer.depSubs;
396
425
  return () => {
397
426
  for (const [depContainer, sub] of subs) {
398
427
  sub.unsubscribe();
399
428
  depContainer.unregisterConsumer(consumerId);
400
- getRegistry().release(sub.Type, sub.key, false, sub.refId);
429
+ registry.release(sub.Type, sub.key, false, sub.refId);
401
430
  }
402
431
  subs.clear();
403
432
  };
404
- }, [consumerId]);
405
- return [
406
- state,
407
- trackedBloc,
408
- componentRef
409
- ];
433
+ }, [consumerId, registry]);
434
+ return [state, trackedBloc];
435
+ }
436
+ const noop = () => {};
437
+ function createConsumer() {
438
+ const consumer = {
439
+ container: null,
440
+ version: 0,
441
+ notify: noop,
442
+ bump: () => {
443
+ consumer.version++;
444
+ consumer.notify();
445
+ },
446
+ getSnapshot: () => consumer.version,
447
+ getServerSnapshot: () => consumer.version,
448
+ interest: emptyPathSet(),
449
+ paths: emptyPathSet(),
450
+ isSelectMode: false,
451
+ selection: null,
452
+ session: /* @__PURE__ */ new Map(),
453
+ depSubs: /* @__PURE__ */ new Map(),
454
+ lastReconcile: null
455
+ };
456
+ return consumer;
410
457
  }
411
458
  /**
412
459
  * Build the per-consumer wrapper that replaces a branded dep handle inside a
@@ -430,10 +477,9 @@ function useBloc(BlocClass, options) {
430
477
  * the dep already has a non-primary session entry this render, reuse its proxy
431
478
  * + union its paths instead of re-acquiring.
432
479
  */
433
- function makeDepWrapper(handle, consumerId, trackedStateRef, sessionRef, onDepHandle) {
480
+ function makeDepWrapper(handle, consumerId, registry, trackedStateRef, consumer, onDepHandle) {
434
481
  const brand = handle[DEP_BRAND];
435
482
  const refId = depRefId(consumerId);
436
- const registry = getRegistry();
437
483
  const perDep = /* @__PURE__ */ new Map();
438
484
  const proxyCache = new ProxyCache();
439
485
  const resolve = (options) => {
@@ -450,7 +496,7 @@ function makeDepWrapper(handle, consumerId, trackedStateRef, sessionRef, onDepHa
450
496
  track: (options) => {
451
497
  const { dep, key, args } = resolve(options);
452
498
  if (trackedStateRef.current == null) return [dep.state, dep];
453
- const session = sessionRef.current;
499
+ const session = consumer.session;
454
500
  const existing = session.get(dep);
455
501
  const tracked = trackRender(dep.state, dep.interner, proxyCache);
456
502
  let cache = perDep.get(dep);
@@ -533,19 +579,39 @@ function expandWithAncestors(paths, interner) {
533
579
  }
534
580
  return expanded;
535
581
  }
536
- let globalConfig = {};
582
+ //#endregion
583
+ //#region src/useBlocDeps.ts
537
584
  /**
538
- * Configure global defaults for `@blac/react` hooks.
585
+ * Feed live values from the component into a bloc's `deps` lane.
586
+ *
587
+ * Each calling component is one owner: its slice is shallow-merged into the
588
+ * bloc's merged `deps` view, and withdrawn when the component unmounts. The
589
+ * bloc observes changes through `onDepsChanged`.
590
+ *
591
+ * ```tsx
592
+ * const [state, bloc] = useBloc(CanvasCubit);
593
+ * useBlocDeps(bloc, { canvas, onTick });
594
+ * ```
539
595
  *
540
- * @param config - Partial configuration to merge with current globals
596
+ * Use this for values a bloc must *read* but should not own — DOM handles,
597
+ * callbacks, values from other hooks. Pass identity-stable values (`useMemo` /
598
+ * `useCallback`) as you would to any hook; a new function identity each render
599
+ * counts as a change.
541
600
  */
542
- function configureBlacReact(config) {
543
- globalConfig = {
544
- ...globalConfig,
545
- ...config
546
- };
601
+ function useBlocDeps(bloc, slice) {
602
+ const ownerId = useId();
603
+ const latest = useRef(slice);
604
+ latest.current = slice;
605
+ useEffect(() => {
606
+ bloc[APPLY_DEPS](ownerId, latest.current);
607
+ });
608
+ useEffect(() => {
609
+ return () => {
610
+ bloc[REMOVE_DEPS_OWNER](ownerId);
611
+ };
612
+ }, [bloc, ownerId]);
547
613
  }
548
614
  //#endregion
549
- export { BlocProvider, configureBlacReact, untracked, useBloc, useProvidedArgs };
615
+ export { BlocProvider, RegistryProvider, untracked, useBloc, useBlocDeps, useProvidedArgs };
550
616
 
551
617
  //# sourceMappingURL=index.js.map