@blac/react 2.0.0-rc-13 → 2.0.0-rc-16
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/package.json +16 -20
- package/src/__tests__/rerenderLogging.test.tsx +257 -0
- package/src/__tests__/useBloc.disposal.test.tsx +377 -0
- package/src/__tests__/useBloc.instanceChange.test.tsx +200 -0
- package/src/__tests__/useBloc.props.test.tsx +166 -144
- package/src/__tests__/useBloc.proxyConfig.test.tsx +5 -5
- package/src/__tests__/useBloc.staticProps.test.tsx +297 -0
- package/src/__tests__/useBloc.tracking.test.tsx +7 -3
- package/src/useBloc.ts +108 -33
- package/src/useExternalBlocStore.ts +14 -9
- package/README.md +0 -265
- package/dist/index.cjs.js +0 -2
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.d.ts +0 -59
- package/dist/index.es.js +0 -111
- package/dist/index.es.js.map +0 -1
package/README.md
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
# @blac/react
|
|
2
|
-
|
|
3
|
-
A powerful React integration for the Blac state management library, providing seamless integration between React components and Blac's reactive state management system.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- 🔄 Automatic re-rendering when relevant state changes
|
|
8
|
-
- 🎯 Fine-grained dependency tracking
|
|
9
|
-
- 🔍 Property access tracking for optimized updates
|
|
10
|
-
- 🎨 TypeScript support with full type inference
|
|
11
|
-
- ⚡️ Efficient state management with minimal boilerplate
|
|
12
|
-
- 🔄 Support for isolated and shared bloc instances
|
|
13
|
-
- 🎯 Custom dependency selectors with access to state, previous state, and instance
|
|
14
|
-
- 🚀 Optimized re-rendering with intelligent snapshot comparison
|
|
15
|
-
|
|
16
|
-
## Important: Arrow Functions Required
|
|
17
|
-
|
|
18
|
-
All methods in Bloc or Cubit classes must use arrow function syntax (`method = () => {}`) instead of the traditional method syntax (`method() {}`). This is because arrow functions automatically bind `this` to the class instance. Without this binding, methods called from React components would lose their context and could not access instance properties like `this.state` or `this.emit()`.
|
|
19
|
-
|
|
20
|
-
```tsx
|
|
21
|
-
// Correct way to define methods in your Bloc/Cubit classes
|
|
22
|
-
class CounterBloc extends Cubit<CounterState> {
|
|
23
|
-
increment = () => {
|
|
24
|
-
this.emit({ ...this.state, count: this.state.count + 1 });
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
decrement = () => {
|
|
28
|
-
this.emit({ ...this.state, count: this.state.count - 1 });
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Incorrect way (will cause issues when called from React):
|
|
33
|
-
class CounterBloc extends Cubit<CounterState> {
|
|
34
|
-
increment() { // ❌ Will lose 'this' context when called from components
|
|
35
|
-
this.emit({ ...this.state, count: this.state.count + 1 });
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Installation
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
npm install @blac/react
|
|
44
|
-
# or
|
|
45
|
-
yarn add @blac/react
|
|
46
|
-
# or
|
|
47
|
-
pnpm add @blac/react
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Quick Start
|
|
51
|
-
|
|
52
|
-
```tsx
|
|
53
|
-
import { useBloc } from '@blac/react';
|
|
54
|
-
import { CounterBloc } from './CounterBloc';
|
|
55
|
-
|
|
56
|
-
function Counter() {
|
|
57
|
-
const [state, counterBloc] = useBloc(CounterBloc);
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
<div>
|
|
61
|
-
<p>Count: {state.count}</p>
|
|
62
|
-
<button onClick={() => counterBloc.increment()}>Increment</button>
|
|
63
|
-
</div>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## Usage
|
|
69
|
-
|
|
70
|
-
### Basic Usage
|
|
71
|
-
|
|
72
|
-
The `useBloc` hook provides a simple way to connect your React components to Blac's state management system:
|
|
73
|
-
|
|
74
|
-
```tsx
|
|
75
|
-
const [state, bloc] = useBloc(YourBloc);
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### Advanced Configuration
|
|
79
|
-
|
|
80
|
-
The hook accepts configuration options for more control:
|
|
81
|
-
|
|
82
|
-
```tsx
|
|
83
|
-
const [state, bloc] = useBloc(YourBloc, {
|
|
84
|
-
id: 'custom-id', // Optional: Custom identifier for the bloc
|
|
85
|
-
props: { /* ... */ }, // Optional: Props to pass to the bloc
|
|
86
|
-
onMount: (bloc) => { /* ... */ }, // Optional: Callback when bloc is mounted (similar to useEffect(<>, []))
|
|
87
|
-
selector: (currentState, previousState, instance) => [/* ... */], // Optional: Custom dependency tracking
|
|
88
|
-
});
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### Automatic Dependency Tracking
|
|
92
|
-
|
|
93
|
-
The hook automatically tracks which state properties and bloc instance properties are accessed in your component and only triggers re-renders when those specific values change:
|
|
94
|
-
|
|
95
|
-
```tsx
|
|
96
|
-
function UserProfile() {
|
|
97
|
-
const [state, userBloc] = useBloc(UserBloc);
|
|
98
|
-
|
|
99
|
-
// Only re-renders when state.name changes
|
|
100
|
-
return <h1>{state.name}</h1>;
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
This also works for getters and computed properties on the Bloc or Cubit class:
|
|
105
|
-
|
|
106
|
-
```tsx
|
|
107
|
-
function UserProfile() {
|
|
108
|
-
const [state, userBloc] = useBloc(UserBloc);
|
|
109
|
-
|
|
110
|
-
// Only re-renders when:
|
|
111
|
-
// - state.firstName changes, OR
|
|
112
|
-
// - state.lastName changes (because the getter accesses these properties)
|
|
113
|
-
return <h1>{userBloc.fullName}</h1>; // Assuming fullName is a getter
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
#### How It Works
|
|
118
|
-
|
|
119
|
-
The dependency tracking system uses JavaScript Proxies to monitor property access during component renders:
|
|
120
|
-
|
|
121
|
-
1. **State Properties**: When you access `state.propertyName`, it's automatically tracked
|
|
122
|
-
2. **Instance Properties**: When you access `bloc.computedValue`, it's automatically tracked
|
|
123
|
-
3. **Intelligent Comparison**: The system separately tracks state dependencies and instance dependencies to handle edge cases where properties are dynamically added/removed
|
|
124
|
-
4. **Optimized Updates**: Components only re-render when tracked dependencies actually change their values
|
|
125
|
-
|
|
126
|
-
#### Configuring Proxy Tracking
|
|
127
|
-
|
|
128
|
-
By default, BlaC uses proxy-based dependency tracking for optimal performance. You can disable this globally if needed:
|
|
129
|
-
|
|
130
|
-
```tsx
|
|
131
|
-
import { Blac } from '@blac/core';
|
|
132
|
-
|
|
133
|
-
// Disable automatic dependency tracking globally
|
|
134
|
-
Blac.setConfig({ proxyDependencyTracking: false });
|
|
135
|
-
|
|
136
|
-
// Now components will re-render on ANY state change
|
|
137
|
-
// unless you provide manual dependencies
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
When proxy tracking is disabled:
|
|
141
|
-
- Components re-render on any state change (similar to traditional state management)
|
|
142
|
-
- Manual dependencies via the `selector` option still work as expected
|
|
143
|
-
- Useful for debugging or when proxy behavior causes issues
|
|
144
|
-
|
|
145
|
-
For more configuration options, see the [@blac/core documentation](https://www.npmjs.com/package/@blac/core).
|
|
146
|
-
|
|
147
|
-
### Custom Dependency Selector
|
|
148
|
-
|
|
149
|
-
For more control over when your component re-renders, you can provide a custom dependency selector. The selector function receives the current state, previous state, and bloc instance, and should return an array of values to track:
|
|
150
|
-
|
|
151
|
-
```tsx
|
|
152
|
-
const [state, bloc] = useBloc(YourBloc, {
|
|
153
|
-
selector: (currentState, previousState, instance) => [
|
|
154
|
-
currentState.specificField,
|
|
155
|
-
currentState.anotherField,
|
|
156
|
-
instance.computedValue // You can also track computed properties from the bloc instance
|
|
157
|
-
]
|
|
158
|
-
});
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
The component will only re-render when any of the values in the returned array change (using `Object.is` comparison, similar to React's `useEffect` dependency array).
|
|
162
|
-
|
|
163
|
-
#### Examples of Custom Selectors
|
|
164
|
-
|
|
165
|
-
**Track only specific state properties:**
|
|
166
|
-
```tsx
|
|
167
|
-
const [state, userBloc] = useBloc(UserBloc, {
|
|
168
|
-
selector: (currentState) => [
|
|
169
|
-
currentState.name,
|
|
170
|
-
currentState.email
|
|
171
|
-
] // Only re-render when name or email changes, ignore other properties
|
|
172
|
-
});
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
**Track computed values:**
|
|
176
|
-
```tsx
|
|
177
|
-
const [state, shoppingCartBloc] = useBloc(ShoppingCartBloc, {
|
|
178
|
-
selector: (currentState, previousState, instance) => [
|
|
179
|
-
instance.totalPrice, // Computed getter
|
|
180
|
-
currentState.items.length // Number of items
|
|
181
|
-
] // Only re-render when total price or item count changes
|
|
182
|
-
});
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
**Compare with previous state:**
|
|
186
|
-
```tsx
|
|
187
|
-
const [state, chatBloc] = useBloc(ChatBloc, {
|
|
188
|
-
selector: (currentState, previousState) => [
|
|
189
|
-
currentState.messages.length > (previousState?.messages.length || 0) ? 'new-message' : 'no-change'
|
|
190
|
-
] // Only re-render when new messages are added, not when existing messages change
|
|
191
|
-
});
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
## API Reference
|
|
195
|
-
|
|
196
|
-
### useBloc Hook
|
|
197
|
-
|
|
198
|
-
```typescript
|
|
199
|
-
function useBloc<B extends BlocConstructor<BlocGeneric>>(
|
|
200
|
-
bloc: B,
|
|
201
|
-
options?: BlocHookOptions<InstanceType<B>>
|
|
202
|
-
): [BlocState<InstanceType<B>>, InstanceType<B>]
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
#### Options
|
|
206
|
-
|
|
207
|
-
- `id?: string` - Custom identifier for the bloc instance
|
|
208
|
-
- `props?: InferPropsFromGeneric<B>` - Props to pass to the bloc
|
|
209
|
-
- `onMount?: (bloc: B) => void` - Callback function invoked when the react component (the consumer) is connected to the bloc instance
|
|
210
|
-
- `selector?: (currentState: BlocState<InstanceType<B>>, previousState: BlocState<InstanceType<B>> | undefined, instance: InstanceType<B>) => unknown[]` - Function to select dependencies for re-renders
|
|
211
|
-
|
|
212
|
-
## Best Practices
|
|
213
|
-
|
|
214
|
-
1. **Use Isolated Blocs**: When you need component-specific state, use isolated blocs:
|
|
215
|
-
```tsx
|
|
216
|
-
class MyIsolatedBloc extends BlocBase {
|
|
217
|
-
static isolated = true;
|
|
218
|
-
// ... rest of your bloc implementation
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
2. **Use Custom Identifiers**: When you need multiple independent instances of the same Bloc type, use custom identifiers to manage different state contexts:
|
|
223
|
-
```tsx
|
|
224
|
-
// In a chat application with multiple chat rooms
|
|
225
|
-
function ChatRoom({ roomId }: { roomId: string }) {
|
|
226
|
-
const [state, chatBloc] = useBloc(ChatBloc, {
|
|
227
|
-
id: `chat-${roomId}`, // Each room gets its own instance
|
|
228
|
-
props: { roomId }
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
return (
|
|
232
|
-
<div>
|
|
233
|
-
<h2>Room: {roomId}</h2>
|
|
234
|
-
{state.messages.map(msg => (
|
|
235
|
-
<Message key={msg.id} message={msg} />
|
|
236
|
-
))}
|
|
237
|
-
</div>
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Usage:
|
|
242
|
-
function ChatApp() {
|
|
243
|
-
return (
|
|
244
|
-
<div>
|
|
245
|
-
<ChatRoom roomId="general" />
|
|
246
|
-
<ChatRoom roomId="support" />
|
|
247
|
-
</div>
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
3. **Choose the Right Dependency Strategy**:
|
|
253
|
-
- **Use automatic tracking** (default) for most cases - it's efficient and requires no setup
|
|
254
|
-
- **Use custom selectors** when you need complex logic, computed comparisons, or want to ignore certain property changes
|
|
255
|
-
- **Avoid custom selectors** for simple property access - automatic tracking is more efficient
|
|
256
|
-
|
|
257
|
-
4. **Type Safety**: Take advantage of TypeScript's type inference for better development experience and catch errors early.
|
|
258
|
-
|
|
259
|
-
## Contributing
|
|
260
|
-
|
|
261
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
262
|
-
|
|
263
|
-
## License
|
|
264
|
-
|
|
265
|
-
MIT
|
package/dist/index.cjs.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const S=require("@blac/core"),r=require("react");function I(n,e){const b=r.useRef(0);b.current++;const l=r.useRef({}),c=r.useMemo(()=>new S.BlacAdapter({componentRef:l,blocConstructor:n},{id:e==null?void 0:e.id,dependencies:e==null?void 0:e.dependencies,props:e==null?void 0:e.props,onMount:e==null?void 0:e.onMount,onUnmount:e==null?void 0:e.onUnmount}),[]);c.resetConsumerTracking();const t=r.useRef(0);r.useEffect(()=>{t.current++,c.options={id:e==null?void 0:e.id,dependencies:e==null?void 0:e.dependencies,props:e==null?void 0:e.props,onMount:e==null?void 0:e.onMount,onUnmount:e==null?void 0:e.onUnmount},(e==null?void 0:e.props)!==void 0&&c.updateProps(e.props)},[e==null?void 0:e.props,e==null?void 0:e.id,e==null?void 0:e.dependencies,e==null?void 0:e.onMount,e==null?void 0:e.onUnmount]);const R=r.useRef(0);r.useEffect(()=>(R.current++,c.mount(),()=>{c.unmount()}),[c.blocInstance]);const B=r.useRef(0),u=r.useMemo(()=>(B.current++,d=>{const y=c.createSubscription({onChange:()=>{d()}});return()=>{y()}}),[c.blocInstance]),a=r.useRef(0),s=r.useRef(0),f=r.useSyncExternalStore(u,()=>(a.current++,c.blocInstance.state),()=>{const d=c.blocInstance;return s.current++,d.state}),m=r.useRef(0),M=r.useMemo(()=>(m.current++,c.getProxyState(f)),[f]),g=r.useRef(0),x=r.useMemo(()=>(g.current++,c.getProxyBlocInstance()),[c.blocInstance]);return r.useEffect(()=>{c.updateLastNotified(l.current)}),[M,x]}function U(n,e={}){const b=r.useRef(`external-${S.generateUUID()}`),l=r.useRef(new Set),c=r.useRef(new Set),t=r.useRef(null),R=r.useMemo(()=>{const u=S.Blac.getInstance();if(n.constructor.isolated||n.isolated){const s=new n(e==null?void 0:e.props),f=(e==null?void 0:e.id)||`${n.name}_${S.generateUUID()}`;return s._updateId(f),u.activateBloc(s),s}return u.getBloc(n,{id:e==null?void 0:e.id,props:e==null?void 0:e.props})},[n,e==null?void 0:e.id]),B=r.useMemo(()=>({getSnapshot:()=>{const u=t.current;return u?u.state:void 0},subscribe:u=>{const a=t.current;if(!a)return()=>{};const s=(m,M)=>{try{l.current.clear(),c.current.clear(),e.selector&&a&&e.selector(m,M,a),u.length>0?u(m):u()}catch(g){console.error("Listener error in useExternalBlocStore:",g)}};return a._observer.subscribe({id:b.current,fn:s})},getServerSnapshot:()=>{const u=t.current;return u?u.state:void 0}}),[]);return t.current=R,{externalStore:B,instance:t,usedKeys:l,usedClassPropKeys:c,rid:b.current}}exports.useBloc=I;exports.useBlocNext=I;exports.useExternalBlocStore=U;
|
|
2
|
-
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.cjs.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../src/useBloc.ts","../src/useExternalBlocStore.ts"],"sourcesContent":["import {\n AdapterOptions,\n BlacAdapter,\n BlocBase,\n BlocConstructor,\n BlocState,\n} from '@blac/core';\nimport { useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\n\n/**\n * Type definition for the return type of the useBloc hook\n */\ntype HookTypes<B extends BlocConstructor<BlocBase<any>>> = [\n BlocState<InstanceType<B>>,\n InstanceType<B>,\n];\n\n/**\n * React hook for integrating with Blac state management\n */\nfunction useBloc<B extends BlocConstructor<BlocBase<any>>>(\n blocConstructor: B,\n options?: {\n props?: ConstructorParameters<B>[0];\n id?: string;\n dependencies?: (bloc: InstanceType<B>) => unknown[];\n onMount?: (bloc: InstanceType<B>) => void;\n onUnmount?: (bloc: InstanceType<B>) => void;\n },\n): HookTypes<B> {\n // Create a unique identifier for this hook instance\n const renderCount = useRef(0);\n renderCount.current++;\n\n const componentRef = useRef<object>({});\n\n // Track adapter creation\n const adapter = useMemo(() => {\n const newAdapter = new BlacAdapter<B>(\n {\n componentRef: componentRef,\n blocConstructor,\n },\n {\n id: options?.id,\n dependencies: options?.dependencies,\n props: options?.props,\n onMount: options?.onMount,\n onUnmount: options?.onUnmount,\n },\n );\n return newAdapter;\n }, []);\n\n // Reset tracking at the start of each render to ensure we only track\n // properties accessed during the current render\n adapter.resetConsumerTracking();\n\n // Track options changes and update props\n const optionsChangeCount = useRef(0);\n useEffect(() => {\n optionsChangeCount.current++;\n adapter.options = {\n id: options?.id,\n dependencies: options?.dependencies,\n props: options?.props,\n onMount: options?.onMount,\n onUnmount: options?.onUnmount,\n };\n\n if (options?.props !== undefined) {\n adapter.updateProps(options.props);\n }\n }, [options?.props, options?.id, options?.dependencies, options?.onMount, options?.onUnmount]);\n\n // Register as consumer and handle lifecycle\n const mountEffectCount = useRef(0);\n useEffect(() => {\n mountEffectCount.current++;\n adapter.mount();\n\n return () => {\n adapter.unmount();\n };\n }, [adapter.blocInstance]);\n\n // Subscribe to state changes using useSyncExternalStore\n const subscribeMemoCount = useRef(0);\n const subscribe = useMemo(() => {\n subscribeMemoCount.current++;\n let subscriptionCount = 0;\n\n return (onStoreChange: () => void) => {\n subscriptionCount++;\n const unsubscribe = adapter.createSubscription({\n onChange: () => {\n onStoreChange();\n },\n });\n\n return () => {\n unsubscribe();\n };\n };\n }, [adapter.blocInstance]);\n\n const snapshotCount = useRef(0);\n const serverSnapshotCount = useRef(0);\n\n const rawState: BlocState<InstanceType<B>> = useSyncExternalStore(\n subscribe,\n // Get snapshot\n () => {\n snapshotCount.current++;\n const bloc = adapter.blocInstance;\n const state = bloc.state;\n return state;\n },\n // Get server snapshot (same as client for now)\n () => {\n const bloc = adapter.blocInstance;\n serverSnapshotCount.current++;\n const state = bloc.state;\n return state;\n },\n );\n\n // Create proxies for fine-grained tracking (if enabled)\n const stateMemoCount = useRef(0);\n const finalState = useMemo(() => {\n stateMemoCount.current++;\n const proxyState = adapter.getProxyState(rawState);\n return proxyState;\n }, [rawState]);\n\n const blocMemoCount = useRef(0);\n const finalBloc = useMemo(() => {\n blocMemoCount.current++;\n const proxyBloc = adapter.getProxyBlocInstance();\n return proxyBloc;\n }, [adapter.blocInstance]);\n\n // Mark consumer as rendered after each render\n useEffect(() => {\n adapter.updateLastNotified(componentRef.current);\n });\n\n // Log final hook return\n return [finalState, finalBloc];\n}\n\nexport default useBloc;\n","import {\n Blac,\n BlocBase,\n BlocConstructor,\n BlocState,\n InferPropsFromGeneric,\n generateUUID,\n} from '@blac/core';\nimport { useCallback, useMemo, useRef } from 'react';\n\ninterface ExternalStoreOptions<B extends BlocBase<any>> {\n id?: string;\n props?: InferPropsFromGeneric<B>;\n selector?: (\n currentState: BlocState<B>,\n previousState: BlocState<B>,\n instance: B,\n ) => any[];\n}\n\ninterface ExternalStore<T> {\n getSnapshot: () => T | undefined;\n subscribe: (listener: () => void) => () => void;\n getServerSnapshot?: () => T | undefined;\n}\n\ninterface ExternalBlocStoreResult<B extends BlocBase<any>> {\n externalStore: ExternalStore<BlocState<B>>;\n instance: { current: B | null };\n usedKeys: { current: Set<string> };\n usedClassPropKeys: { current: Set<string> };\n rid: string;\n}\n\n/**\n * Hook for using an external bloc store\n * Provides low-level access to bloc instances for use with React's useSyncExternalStore\n */\nexport default function useExternalBlocStore<\n B extends BlocConstructor<BlocBase<any>>,\n>(\n blocConstructor: B,\n options: ExternalStoreOptions<InstanceType<B>> = {},\n): ExternalBlocStoreResult<InstanceType<B>> {\n const ridRef = useRef<string>(`external-${generateUUID()}`);\n const usedKeysRef = useRef<Set<string>>(new Set());\n const usedClassPropKeysRef = useRef<Set<string>>(new Set());\n const instanceRef = useRef<InstanceType<B> | null>(null);\n\n // Get or create bloc instance\n const bloc = useMemo(() => {\n const blac = Blac.getInstance();\n const base = blocConstructor as unknown as BlocBase<any>;\n\n // For isolated blocs, always create a new instance\n if (\n (base.constructor as any).isolated ||\n (blocConstructor as any).isolated\n ) {\n const newBloc = new blocConstructor(options?.props) as InstanceType<B>;\n const uniqueId =\n options?.id || `${blocConstructor.name}_${generateUUID()}`;\n newBloc._updateId(uniqueId);\n blac.activateBloc(newBloc);\n return newBloc;\n }\n\n // For shared blocs, use the existing getBloc logic\n return blac.getBloc(blocConstructor, {\n id: options?.id,\n props: options?.props,\n });\n }, [blocConstructor, options?.id]);\n\n // Create external store interface\n const externalStore = useMemo<ExternalStore<BlocState<InstanceType<B>>>>(\n () => ({\n getSnapshot: () => {\n const currentInstance = instanceRef.current;\n return currentInstance ? currentInstance.state : undefined;\n },\n subscribe: (listener: () => void) => {\n const currentInstance = instanceRef.current;\n if (!currentInstance) {\n // Return no-op unsubscribe if no instance\n return () => {};\n }\n\n // Wrap listener to handle errors gracefully\n const safeListener = (\n newState: BlocState<InstanceType<B>>,\n oldState: BlocState<InstanceType<B>>,\n ) => {\n try {\n // Reset tracking keys on each listener call\n usedKeysRef.current.clear();\n usedClassPropKeysRef.current.clear();\n\n // Call selector if provided\n if (options.selector && currentInstance) {\n options.selector(newState, oldState, currentInstance);\n }\n\n // Call listener with state if it expects it\n if (listener.length > 0) {\n (listener as any)(newState);\n } else {\n listener();\n }\n } catch (error) {\n console.error('Listener error in useExternalBlocStore:', error);\n // Don't rethrow to prevent breaking other listeners\n }\n };\n\n const unsubscribe = currentInstance._observer.subscribe({\n id: ridRef.current,\n fn: safeListener,\n });\n return unsubscribe;\n },\n getServerSnapshot: () => {\n const currentInstance = instanceRef.current;\n return currentInstance ? currentInstance.state : undefined;\n },\n }),\n [],\n );\n\n // Update instance ref\n instanceRef.current = bloc;\n\n return {\n externalStore,\n instance: instanceRef,\n usedKeys: usedKeysRef,\n usedClassPropKeys: usedClassPropKeysRef,\n rid: ridRef.current,\n };\n}\n"],"names":["useBloc","blocConstructor","options","renderCount","useRef","componentRef","adapter","useMemo","BlacAdapter","optionsChangeCount","useEffect","mountEffectCount","subscribeMemoCount","subscribe","onStoreChange","unsubscribe","snapshotCount","serverSnapshotCount","rawState","useSyncExternalStore","bloc","stateMemoCount","finalState","blocMemoCount","finalBloc","useExternalBlocStore","ridRef","generateUUID","usedKeysRef","usedClassPropKeysRef","instanceRef","blac","Blac","newBloc","uniqueId","externalStore","currentInstance","listener","safeListener","newState","oldState","error"],"mappings":"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"}
|
package/dist/index.d.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { BlocBase } from '@blac/core';
|
|
2
|
-
import { BlocConstructor } from '@blac/core';
|
|
3
|
-
import { BlocState } from '@blac/core';
|
|
4
|
-
import { InferPropsFromGeneric } from '@blac/core';
|
|
5
|
-
|
|
6
|
-
declare interface ExternalBlocStoreResult<B extends BlocBase<any>> {
|
|
7
|
-
externalStore: ExternalStore<BlocState<B>>;
|
|
8
|
-
instance: {
|
|
9
|
-
current: B | null;
|
|
10
|
-
};
|
|
11
|
-
usedKeys: {
|
|
12
|
-
current: Set<string>;
|
|
13
|
-
};
|
|
14
|
-
usedClassPropKeys: {
|
|
15
|
-
current: Set<string>;
|
|
16
|
-
};
|
|
17
|
-
rid: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
declare interface ExternalStore<T> {
|
|
21
|
-
getSnapshot: () => T | undefined;
|
|
22
|
-
subscribe: (listener: () => void) => () => void;
|
|
23
|
-
getServerSnapshot?: () => T | undefined;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
declare interface ExternalStoreOptions<B extends BlocBase<any>> {
|
|
27
|
-
id?: string;
|
|
28
|
-
props?: InferPropsFromGeneric<B>;
|
|
29
|
-
selector?: (currentState: BlocState<B>, previousState: BlocState<B>, instance: B) => any[];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Type definition for the return type of the useBloc hook
|
|
34
|
-
*/
|
|
35
|
-
declare type HookTypes<B extends BlocConstructor<BlocBase<any>>> = [
|
|
36
|
-
BlocState<InstanceType<B>>,
|
|
37
|
-
InstanceType<B>
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* React hook for integrating with Blac state management
|
|
42
|
-
*/
|
|
43
|
-
declare function useBloc<B extends BlocConstructor<BlocBase<any>>>(blocConstructor: B, options?: {
|
|
44
|
-
props?: ConstructorParameters<B>[0];
|
|
45
|
-
id?: string;
|
|
46
|
-
dependencies?: (bloc: InstanceType<B>) => unknown[];
|
|
47
|
-
onMount?: (bloc: InstanceType<B>) => void;
|
|
48
|
-
onUnmount?: (bloc: InstanceType<B>) => void;
|
|
49
|
-
}): HookTypes<B>;
|
|
50
|
-
export { useBloc }
|
|
51
|
-
export { useBloc as useBlocNext }
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Hook for using an external bloc store
|
|
55
|
-
* Provides low-level access to bloc instances for use with React's useSyncExternalStore
|
|
56
|
-
*/
|
|
57
|
-
export declare function useExternalBlocStore<B extends BlocConstructor<BlocBase<any>>>(blocConstructor: B, options?: ExternalStoreOptions<InstanceType<B>>): ExternalBlocStoreResult<InstanceType<B>>;
|
|
58
|
-
|
|
59
|
-
export { }
|
package/dist/index.es.js
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { BlacAdapter as v, generateUUID as h, Blac as w } from "@blac/core";
|
|
2
|
-
import { useRef as u, useMemo as b, useEffect as M, useSyncExternalStore as U } from "react";
|
|
3
|
-
function P(n, e) {
|
|
4
|
-
const m = u(0);
|
|
5
|
-
m.current++;
|
|
6
|
-
const f = u({}), r = b(() => new v(
|
|
7
|
-
{
|
|
8
|
-
componentRef: f,
|
|
9
|
-
blocConstructor: n
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
id: e == null ? void 0 : e.id,
|
|
13
|
-
dependencies: e == null ? void 0 : e.dependencies,
|
|
14
|
-
props: e == null ? void 0 : e.props,
|
|
15
|
-
onMount: e == null ? void 0 : e.onMount,
|
|
16
|
-
onUnmount: e == null ? void 0 : e.onUnmount
|
|
17
|
-
}
|
|
18
|
-
), []);
|
|
19
|
-
r.resetConsumerTracking();
|
|
20
|
-
const t = u(0);
|
|
21
|
-
M(() => {
|
|
22
|
-
t.current++, r.options = {
|
|
23
|
-
id: e == null ? void 0 : e.id,
|
|
24
|
-
dependencies: e == null ? void 0 : e.dependencies,
|
|
25
|
-
props: e == null ? void 0 : e.props,
|
|
26
|
-
onMount: e == null ? void 0 : e.onMount,
|
|
27
|
-
onUnmount: e == null ? void 0 : e.onUnmount
|
|
28
|
-
}, (e == null ? void 0 : e.props) !== void 0 && r.updateProps(e.props);
|
|
29
|
-
}, [e == null ? void 0 : e.props, e == null ? void 0 : e.id, e == null ? void 0 : e.dependencies, e == null ? void 0 : e.onMount, e == null ? void 0 : e.onUnmount]);
|
|
30
|
-
const x = u(0);
|
|
31
|
-
M(() => (x.current++, r.mount(), () => {
|
|
32
|
-
r.unmount();
|
|
33
|
-
}), [r.blocInstance]);
|
|
34
|
-
const B = u(0), c = b(() => (B.current++, (l) => {
|
|
35
|
-
const g = r.createSubscription({
|
|
36
|
-
onChange: () => {
|
|
37
|
-
l();
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
return () => {
|
|
41
|
-
g();
|
|
42
|
-
};
|
|
43
|
-
}), [r.blocInstance]), a = u(0), d = u(0), s = U(
|
|
44
|
-
c,
|
|
45
|
-
// Get snapshot
|
|
46
|
-
() => (a.current++, r.blocInstance.state),
|
|
47
|
-
// Get server snapshot (same as client for now)
|
|
48
|
-
() => {
|
|
49
|
-
const l = r.blocInstance;
|
|
50
|
-
return d.current++, l.state;
|
|
51
|
-
}
|
|
52
|
-
), S = u(0), I = b(() => (S.current++, r.getProxyState(s)), [s]), y = u(0), C = b(() => (y.current++, r.getProxyBlocInstance()), [r.blocInstance]);
|
|
53
|
-
return M(() => {
|
|
54
|
-
r.updateLastNotified(f.current);
|
|
55
|
-
}), [I, C];
|
|
56
|
-
}
|
|
57
|
-
function K(n, e = {}) {
|
|
58
|
-
const m = u(`external-${h()}`), f = u(/* @__PURE__ */ new Set()), r = u(/* @__PURE__ */ new Set()), t = u(null), x = b(() => {
|
|
59
|
-
const c = w.getInstance();
|
|
60
|
-
if (n.constructor.isolated || n.isolated) {
|
|
61
|
-
const d = new n(e == null ? void 0 : e.props), s = (e == null ? void 0 : e.id) || `${n.name}_${h()}`;
|
|
62
|
-
return d._updateId(s), c.activateBloc(d), d;
|
|
63
|
-
}
|
|
64
|
-
return c.getBloc(n, {
|
|
65
|
-
id: e == null ? void 0 : e.id,
|
|
66
|
-
props: e == null ? void 0 : e.props
|
|
67
|
-
});
|
|
68
|
-
}, [n, e == null ? void 0 : e.id]), B = b(
|
|
69
|
-
() => ({
|
|
70
|
-
getSnapshot: () => {
|
|
71
|
-
const c = t.current;
|
|
72
|
-
return c ? c.state : void 0;
|
|
73
|
-
},
|
|
74
|
-
subscribe: (c) => {
|
|
75
|
-
const a = t.current;
|
|
76
|
-
if (!a)
|
|
77
|
-
return () => {
|
|
78
|
-
};
|
|
79
|
-
const d = (S, I) => {
|
|
80
|
-
try {
|
|
81
|
-
f.current.clear(), r.current.clear(), e.selector && a && e.selector(S, I, a), c.length > 0 ? c(S) : c();
|
|
82
|
-
} catch (y) {
|
|
83
|
-
console.error("Listener error in useExternalBlocStore:", y);
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
return a._observer.subscribe({
|
|
87
|
-
id: m.current,
|
|
88
|
-
fn: d
|
|
89
|
-
});
|
|
90
|
-
},
|
|
91
|
-
getServerSnapshot: () => {
|
|
92
|
-
const c = t.current;
|
|
93
|
-
return c ? c.state : void 0;
|
|
94
|
-
}
|
|
95
|
-
}),
|
|
96
|
-
[]
|
|
97
|
-
);
|
|
98
|
-
return t.current = x, {
|
|
99
|
-
externalStore: B,
|
|
100
|
-
instance: t,
|
|
101
|
-
usedKeys: f,
|
|
102
|
-
usedClassPropKeys: r,
|
|
103
|
-
rid: m.current
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
export {
|
|
107
|
-
P as useBloc,
|
|
108
|
-
P as useBlocNext,
|
|
109
|
-
K as useExternalBlocStore
|
|
110
|
-
};
|
|
111
|
-
//# sourceMappingURL=index.es.js.map
|
package/dist/index.es.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.es.js","sources":["../src/useBloc.ts","../src/useExternalBlocStore.ts"],"sourcesContent":["import {\n AdapterOptions,\n BlacAdapter,\n BlocBase,\n BlocConstructor,\n BlocState,\n} from '@blac/core';\nimport { useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\n\n/**\n * Type definition for the return type of the useBloc hook\n */\ntype HookTypes<B extends BlocConstructor<BlocBase<any>>> = [\n BlocState<InstanceType<B>>,\n InstanceType<B>,\n];\n\n/**\n * React hook for integrating with Blac state management\n */\nfunction useBloc<B extends BlocConstructor<BlocBase<any>>>(\n blocConstructor: B,\n options?: {\n props?: ConstructorParameters<B>[0];\n id?: string;\n dependencies?: (bloc: InstanceType<B>) => unknown[];\n onMount?: (bloc: InstanceType<B>) => void;\n onUnmount?: (bloc: InstanceType<B>) => void;\n },\n): HookTypes<B> {\n // Create a unique identifier for this hook instance\n const renderCount = useRef(0);\n renderCount.current++;\n\n const componentRef = useRef<object>({});\n\n // Track adapter creation\n const adapter = useMemo(() => {\n const newAdapter = new BlacAdapter<B>(\n {\n componentRef: componentRef,\n blocConstructor,\n },\n {\n id: options?.id,\n dependencies: options?.dependencies,\n props: options?.props,\n onMount: options?.onMount,\n onUnmount: options?.onUnmount,\n },\n );\n return newAdapter;\n }, []);\n\n // Reset tracking at the start of each render to ensure we only track\n // properties accessed during the current render\n adapter.resetConsumerTracking();\n\n // Track options changes and update props\n const optionsChangeCount = useRef(0);\n useEffect(() => {\n optionsChangeCount.current++;\n adapter.options = {\n id: options?.id,\n dependencies: options?.dependencies,\n props: options?.props,\n onMount: options?.onMount,\n onUnmount: options?.onUnmount,\n };\n\n if (options?.props !== undefined) {\n adapter.updateProps(options.props);\n }\n }, [options?.props, options?.id, options?.dependencies, options?.onMount, options?.onUnmount]);\n\n // Register as consumer and handle lifecycle\n const mountEffectCount = useRef(0);\n useEffect(() => {\n mountEffectCount.current++;\n adapter.mount();\n\n return () => {\n adapter.unmount();\n };\n }, [adapter.blocInstance]);\n\n // Subscribe to state changes using useSyncExternalStore\n const subscribeMemoCount = useRef(0);\n const subscribe = useMemo(() => {\n subscribeMemoCount.current++;\n let subscriptionCount = 0;\n\n return (onStoreChange: () => void) => {\n subscriptionCount++;\n const unsubscribe = adapter.createSubscription({\n onChange: () => {\n onStoreChange();\n },\n });\n\n return () => {\n unsubscribe();\n };\n };\n }, [adapter.blocInstance]);\n\n const snapshotCount = useRef(0);\n const serverSnapshotCount = useRef(0);\n\n const rawState: BlocState<InstanceType<B>> = useSyncExternalStore(\n subscribe,\n // Get snapshot\n () => {\n snapshotCount.current++;\n const bloc = adapter.blocInstance;\n const state = bloc.state;\n return state;\n },\n // Get server snapshot (same as client for now)\n () => {\n const bloc = adapter.blocInstance;\n serverSnapshotCount.current++;\n const state = bloc.state;\n return state;\n },\n );\n\n // Create proxies for fine-grained tracking (if enabled)\n const stateMemoCount = useRef(0);\n const finalState = useMemo(() => {\n stateMemoCount.current++;\n const proxyState = adapter.getProxyState(rawState);\n return proxyState;\n }, [rawState]);\n\n const blocMemoCount = useRef(0);\n const finalBloc = useMemo(() => {\n blocMemoCount.current++;\n const proxyBloc = adapter.getProxyBlocInstance();\n return proxyBloc;\n }, [adapter.blocInstance]);\n\n // Mark consumer as rendered after each render\n useEffect(() => {\n adapter.updateLastNotified(componentRef.current);\n });\n\n // Log final hook return\n return [finalState, finalBloc];\n}\n\nexport default useBloc;\n","import {\n Blac,\n BlocBase,\n BlocConstructor,\n BlocState,\n InferPropsFromGeneric,\n generateUUID,\n} from '@blac/core';\nimport { useCallback, useMemo, useRef } from 'react';\n\ninterface ExternalStoreOptions<B extends BlocBase<any>> {\n id?: string;\n props?: InferPropsFromGeneric<B>;\n selector?: (\n currentState: BlocState<B>,\n previousState: BlocState<B>,\n instance: B,\n ) => any[];\n}\n\ninterface ExternalStore<T> {\n getSnapshot: () => T | undefined;\n subscribe: (listener: () => void) => () => void;\n getServerSnapshot?: () => T | undefined;\n}\n\ninterface ExternalBlocStoreResult<B extends BlocBase<any>> {\n externalStore: ExternalStore<BlocState<B>>;\n instance: { current: B | null };\n usedKeys: { current: Set<string> };\n usedClassPropKeys: { current: Set<string> };\n rid: string;\n}\n\n/**\n * Hook for using an external bloc store\n * Provides low-level access to bloc instances for use with React's useSyncExternalStore\n */\nexport default function useExternalBlocStore<\n B extends BlocConstructor<BlocBase<any>>,\n>(\n blocConstructor: B,\n options: ExternalStoreOptions<InstanceType<B>> = {},\n): ExternalBlocStoreResult<InstanceType<B>> {\n const ridRef = useRef<string>(`external-${generateUUID()}`);\n const usedKeysRef = useRef<Set<string>>(new Set());\n const usedClassPropKeysRef = useRef<Set<string>>(new Set());\n const instanceRef = useRef<InstanceType<B> | null>(null);\n\n // Get or create bloc instance\n const bloc = useMemo(() => {\n const blac = Blac.getInstance();\n const base = blocConstructor as unknown as BlocBase<any>;\n\n // For isolated blocs, always create a new instance\n if (\n (base.constructor as any).isolated ||\n (blocConstructor as any).isolated\n ) {\n const newBloc = new blocConstructor(options?.props) as InstanceType<B>;\n const uniqueId =\n options?.id || `${blocConstructor.name}_${generateUUID()}`;\n newBloc._updateId(uniqueId);\n blac.activateBloc(newBloc);\n return newBloc;\n }\n\n // For shared blocs, use the existing getBloc logic\n return blac.getBloc(blocConstructor, {\n id: options?.id,\n props: options?.props,\n });\n }, [blocConstructor, options?.id]);\n\n // Create external store interface\n const externalStore = useMemo<ExternalStore<BlocState<InstanceType<B>>>>(\n () => ({\n getSnapshot: () => {\n const currentInstance = instanceRef.current;\n return currentInstance ? currentInstance.state : undefined;\n },\n subscribe: (listener: () => void) => {\n const currentInstance = instanceRef.current;\n if (!currentInstance) {\n // Return no-op unsubscribe if no instance\n return () => {};\n }\n\n // Wrap listener to handle errors gracefully\n const safeListener = (\n newState: BlocState<InstanceType<B>>,\n oldState: BlocState<InstanceType<B>>,\n ) => {\n try {\n // Reset tracking keys on each listener call\n usedKeysRef.current.clear();\n usedClassPropKeysRef.current.clear();\n\n // Call selector if provided\n if (options.selector && currentInstance) {\n options.selector(newState, oldState, currentInstance);\n }\n\n // Call listener with state if it expects it\n if (listener.length > 0) {\n (listener as any)(newState);\n } else {\n listener();\n }\n } catch (error) {\n console.error('Listener error in useExternalBlocStore:', error);\n // Don't rethrow to prevent breaking other listeners\n }\n };\n\n const unsubscribe = currentInstance._observer.subscribe({\n id: ridRef.current,\n fn: safeListener,\n });\n return unsubscribe;\n },\n getServerSnapshot: () => {\n const currentInstance = instanceRef.current;\n return currentInstance ? currentInstance.state : undefined;\n },\n }),\n [],\n );\n\n // Update instance ref\n instanceRef.current = bloc;\n\n return {\n externalStore,\n instance: instanceRef,\n usedKeys: usedKeysRef,\n usedClassPropKeys: usedClassPropKeysRef,\n rid: ridRef.current,\n };\n}\n"],"names":["useBloc","blocConstructor","options","renderCount","useRef","componentRef","adapter","useMemo","BlacAdapter","optionsChangeCount","useEffect","mountEffectCount","subscribeMemoCount","subscribe","onStoreChange","unsubscribe","snapshotCount","serverSnapshotCount","rawState","useSyncExternalStore","bloc","stateMemoCount","finalState","blocMemoCount","finalBloc","useExternalBlocStore","ridRef","generateUUID","usedKeysRef","usedClassPropKeysRef","instanceRef","blac","Blac","newBloc","uniqueId","externalStore","currentInstance","listener","safeListener","newState","oldState","error"],"mappings":"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"}
|