@blac/react 2.0.0-rc-11 → 2.0.0-rc-14
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.cjs.js +2 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.es.js +105 -75
- package/dist/index.es.js.map +1 -1
- package/package.json +12 -12
- 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/index.ts +1 -0
- package/src/useBloc.ts +108 -33
- package/src/useExternalBlocStore.ts +14 -9
- package/README.md +0 -265
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Blac,
|
|
3
|
+
Bloc,
|
|
4
|
+
BlocConstructorParams,
|
|
5
|
+
Cubit,
|
|
6
|
+
PropsUpdated,
|
|
7
|
+
} from '@blac/core';
|
|
2
8
|
import { act, renderHook, waitFor } from '@testing-library/react';
|
|
3
|
-
import React from 'react';
|
|
4
9
|
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
|
5
10
|
import useBloc from '../useBloc';
|
|
6
11
|
|
|
@@ -16,8 +21,15 @@ interface SearchState {
|
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
class SearchBloc extends Bloc<SearchState, PropsUpdated<SearchProps>> {
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
props: BlocConstructorParams<typeof SearchBloc>;
|
|
25
|
+
|
|
26
|
+
constructor(props?: SearchProps) {
|
|
27
|
+
// Initialize with props
|
|
28
|
+
super({
|
|
29
|
+
results: props ? [`Search: ${props.query}`] : [],
|
|
30
|
+
loading: false,
|
|
31
|
+
});
|
|
32
|
+
this.props = props;
|
|
21
33
|
|
|
22
34
|
this.on(PropsUpdated<SearchProps>, (event, emit) => {
|
|
23
35
|
emit({ results: [`Search: ${event.props.query}`], loading: false });
|
|
@@ -34,12 +46,18 @@ interface CounterState {
|
|
|
34
46
|
stepSize: number;
|
|
35
47
|
}
|
|
36
48
|
|
|
37
|
-
class CounterCubit extends Cubit<CounterState
|
|
38
|
-
constructor() {
|
|
39
|
-
super({ count: 0, stepSize: 1 });
|
|
49
|
+
class CounterCubit extends Cubit<CounterState> {
|
|
50
|
+
constructor(props?: CounterProps) {
|
|
51
|
+
super({ count: 0, stepSize: props?.step ?? 1 });
|
|
52
|
+
this.props = props || null;
|
|
40
53
|
}
|
|
41
54
|
|
|
42
|
-
|
|
55
|
+
props: CounterProps | null = null;
|
|
56
|
+
|
|
57
|
+
protected onPropsChanged(
|
|
58
|
+
oldProps: CounterProps | undefined,
|
|
59
|
+
newProps: CounterProps,
|
|
60
|
+
): void {
|
|
43
61
|
if (oldProps?.step !== newProps.step) {
|
|
44
62
|
this.emit({ ...this.state, stepSize: newProps.step });
|
|
45
63
|
}
|
|
@@ -47,7 +65,11 @@ class CounterCubit extends Cubit<CounterState, CounterProps> {
|
|
|
47
65
|
|
|
48
66
|
increment = () => {
|
|
49
67
|
const step = this.props?.step ?? 1;
|
|
50
|
-
this.emit({
|
|
68
|
+
this.emit({
|
|
69
|
+
...this.state,
|
|
70
|
+
count: this.state.count + step,
|
|
71
|
+
stepSize: step,
|
|
72
|
+
});
|
|
51
73
|
};
|
|
52
74
|
}
|
|
53
75
|
|
|
@@ -59,65 +81,61 @@ describe('useBloc props integration', () => {
|
|
|
59
81
|
describe('Basic props functionality', () => {
|
|
60
82
|
it('should pass props to Bloc via adapter', async () => {
|
|
61
83
|
const { result } = renderHook(
|
|
62
|
-
({ query }) => useBloc(
|
|
63
|
-
|
|
64
|
-
{ props: { apiEndpoint: '/api/search', query } }
|
|
65
|
-
),
|
|
66
|
-
{ initialProps: { query: 'initial' } }
|
|
84
|
+
({ query }) => useBloc(SearchBloc, { staticProps: { query } }),
|
|
85
|
+
{ initialProps: { query: 'initial' } },
|
|
67
86
|
);
|
|
68
87
|
|
|
69
88
|
await act(async () => {
|
|
70
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
89
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
71
90
|
});
|
|
72
91
|
|
|
73
92
|
const [state] = result.current;
|
|
74
93
|
expect(state.results).toEqual(['Search: initial']);
|
|
75
94
|
});
|
|
76
95
|
|
|
77
|
-
it('should
|
|
96
|
+
it('should create new instance when staticProps change', async () => {
|
|
78
97
|
const { result, rerender } = renderHook(
|
|
79
|
-
({ query }) => useBloc(
|
|
80
|
-
|
|
81
|
-
{ props: { apiEndpoint: '/api/search', query } }
|
|
82
|
-
),
|
|
83
|
-
{ initialProps: { query: 'initial' } }
|
|
98
|
+
({ query }) => useBloc(SearchBloc, { staticProps: { query } }),
|
|
99
|
+
{ initialProps: { query: 'initial' } },
|
|
84
100
|
);
|
|
85
101
|
|
|
86
102
|
await act(async () => {
|
|
87
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
103
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
88
104
|
});
|
|
89
105
|
|
|
106
|
+
const firstInstance = result.current[1];
|
|
90
107
|
expect(result.current[0].results).toEqual(['Search: initial']);
|
|
91
108
|
|
|
92
109
|
rerender({ query: 'updated' });
|
|
93
110
|
|
|
94
111
|
await act(async () => {
|
|
95
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
112
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
96
113
|
});
|
|
97
114
|
|
|
115
|
+
const secondInstance = result.current[1];
|
|
116
|
+
expect(secondInstance).not.toBe(firstInstance); // New instance created
|
|
98
117
|
expect(result.current[0].results).toEqual(['Search: updated']);
|
|
99
118
|
});
|
|
100
119
|
|
|
101
120
|
it('should work with Cubit props', async () => {
|
|
102
121
|
const { result } = renderHook(
|
|
103
122
|
({ step }) => {
|
|
104
|
-
const [state, cubit] = useBloc(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
);
|
|
123
|
+
const [state, cubit] = useBloc(CounterCubit, {
|
|
124
|
+
staticProps: { step },
|
|
125
|
+
});
|
|
108
126
|
// Access count to ensure it's tracked
|
|
109
127
|
void state.count;
|
|
110
128
|
return [state, cubit];
|
|
111
129
|
},
|
|
112
|
-
{ initialProps: { step: 1 } }
|
|
130
|
+
{ initialProps: { step: 1 } },
|
|
113
131
|
);
|
|
114
132
|
|
|
115
133
|
// Wait for props to be set
|
|
116
134
|
await act(async () => {
|
|
117
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
135
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
118
136
|
});
|
|
119
137
|
|
|
120
|
-
const [state, cubit] = result.current;
|
|
138
|
+
const [state, cubit] = result.current as [CounterState, CounterCubit];
|
|
121
139
|
expect(state.stepSize).toBe(1);
|
|
122
140
|
|
|
123
141
|
act(() => {
|
|
@@ -126,138 +144,142 @@ describe('useBloc props integration', () => {
|
|
|
126
144
|
|
|
127
145
|
// Wait for React to re-render with new state
|
|
128
146
|
await waitFor(() => {
|
|
129
|
-
expect(result.current[0].count).toBe(1);
|
|
147
|
+
expect((result.current[0] as CounterState).count).toBe(1);
|
|
130
148
|
});
|
|
131
149
|
});
|
|
132
150
|
|
|
133
|
-
it('should
|
|
151
|
+
it('should create new Cubit instance when staticProps change', async () => {
|
|
134
152
|
const { result, rerender } = renderHook(
|
|
135
153
|
({ step }) => {
|
|
136
|
-
const [state, cubit] = useBloc(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
);
|
|
154
|
+
const [state, cubit] = useBloc(CounterCubit, {
|
|
155
|
+
staticProps: { step },
|
|
156
|
+
});
|
|
140
157
|
// Access count to ensure it's tracked
|
|
141
158
|
void state.count;
|
|
142
159
|
return [state, cubit];
|
|
143
160
|
},
|
|
144
|
-
{ initialProps: { step: 1 } }
|
|
161
|
+
{ initialProps: { step: 1 } },
|
|
145
162
|
);
|
|
146
163
|
|
|
147
164
|
// Wait for initial props to be set
|
|
148
165
|
await act(async () => {
|
|
149
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
166
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
150
167
|
});
|
|
151
168
|
|
|
152
|
-
|
|
169
|
+
const firstInstance = result.current[1];
|
|
170
|
+
expect((result.current[0] as CounterState).stepSize).toBe(1);
|
|
153
171
|
|
|
154
172
|
rerender({ step: 5 });
|
|
155
173
|
|
|
156
|
-
// Wait for
|
|
174
|
+
// Wait for new instance to be created
|
|
157
175
|
await act(async () => {
|
|
158
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
176
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
159
177
|
});
|
|
160
178
|
|
|
161
|
-
|
|
179
|
+
const secondInstance = result.current[1];
|
|
180
|
+
expect(secondInstance).not.toBe(firstInstance); // New instance
|
|
181
|
+
expect((result.current[0] as CounterState).stepSize).toBe(5);
|
|
182
|
+
expect((result.current[0] as CounterState).count).toBe(0); // New instance starts at 0
|
|
162
183
|
|
|
163
184
|
act(() => {
|
|
164
|
-
result.current[1].increment();
|
|
185
|
+
(result.current[1] as CounterCubit).increment();
|
|
165
186
|
});
|
|
166
187
|
|
|
167
188
|
// Wait for React to re-render with new state
|
|
168
189
|
await waitFor(() => {
|
|
169
|
-
expect(result.current[0].count).toBe(5);
|
|
190
|
+
expect((result.current[0] as CounterState).count).toBe(5);
|
|
170
191
|
});
|
|
171
192
|
});
|
|
172
193
|
});
|
|
173
194
|
|
|
174
|
-
describe('
|
|
175
|
-
it('should
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
// First hook owns props
|
|
195
|
+
describe('Instance sharing', () => {
|
|
196
|
+
it('should share instance when using same instanceId', async () => {
|
|
197
|
+
// First hook creates instance with explicit id
|
|
179
198
|
const { result: result1 } = renderHook(() =>
|
|
180
|
-
useBloc(
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
)
|
|
199
|
+
useBloc(SearchBloc, {
|
|
200
|
+
staticProps: { query: 'first' },
|
|
201
|
+
instanceId: 'shared-search',
|
|
202
|
+
}),
|
|
184
203
|
);
|
|
185
204
|
|
|
186
|
-
// Second hook
|
|
205
|
+
// Second hook uses same instanceId - gets same instance
|
|
187
206
|
const { result: result2 } = renderHook(() =>
|
|
188
|
-
useBloc(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
)
|
|
207
|
+
useBloc(SearchBloc, {
|
|
208
|
+
staticProps: { query: 'second' }, // Different props, but same instanceId
|
|
209
|
+
instanceId: 'shared-search',
|
|
210
|
+
}),
|
|
192
211
|
);
|
|
193
212
|
|
|
194
213
|
await act(async () => {
|
|
195
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
214
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
196
215
|
});
|
|
197
216
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
// Both should see the owner's state
|
|
203
|
-
expect(result1.current[0].results).toEqual(['Search: owner']);
|
|
204
|
-
expect(result2.current[0].results).toEqual(['Search: owner']);
|
|
217
|
+
// Both should share the same instance (check by ID)
|
|
218
|
+
expect(result1.current[1]._id).toBe('shared-search');
|
|
219
|
+
expect(result2.current[1]._id).toBe('shared-search');
|
|
205
220
|
|
|
206
|
-
|
|
221
|
+
// Both see the state from the first instance (query: 'first')
|
|
222
|
+
expect(result1.current[0].results).toEqual(['Search: first']);
|
|
223
|
+
expect(result2.current[0].results).toEqual(['Search: first']);
|
|
207
224
|
});
|
|
208
225
|
|
|
209
|
-
it('should allow
|
|
210
|
-
//
|
|
211
|
-
const { result:
|
|
212
|
-
useBloc(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
)
|
|
226
|
+
it('should allow consumers without staticProps to share instance', async () => {
|
|
227
|
+
// First consumer with props and explicit id
|
|
228
|
+
const { result: firstResult } = renderHook(() =>
|
|
229
|
+
useBloc(SearchBloc, {
|
|
230
|
+
staticProps: { query: 'test' },
|
|
231
|
+
instanceId: 'search-instance',
|
|
232
|
+
}),
|
|
216
233
|
);
|
|
217
234
|
|
|
218
|
-
//
|
|
219
|
-
const { result:
|
|
220
|
-
useBloc(SearchBloc, {
|
|
235
|
+
// Second consumer without props but same instanceId
|
|
236
|
+
const { result: secondResult } = renderHook(() =>
|
|
237
|
+
useBloc(SearchBloc, { instanceId: 'search-instance' }),
|
|
221
238
|
);
|
|
222
239
|
|
|
223
240
|
await act(async () => {
|
|
224
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
241
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
225
242
|
});
|
|
226
243
|
|
|
227
|
-
//
|
|
228
|
-
expect(
|
|
229
|
-
expect(
|
|
244
|
+
// Same instance (check by ID)
|
|
245
|
+
expect(firstResult.current[1]._id).toBe('search-instance');
|
|
246
|
+
expect(secondResult.current[1]._id).toBe('search-instance');
|
|
247
|
+
|
|
248
|
+
// Both see the same state
|
|
249
|
+
expect(secondResult.current[0].results).toEqual(['Search: test']);
|
|
230
250
|
});
|
|
231
251
|
|
|
232
|
-
it('should
|
|
252
|
+
it('should create independent instances with different staticProps', async () => {
|
|
233
253
|
const { result: result1, unmount: unmount1 } = renderHook(() =>
|
|
234
|
-
useBloc(
|
|
235
|
-
SearchBloc,
|
|
236
|
-
{ props: { apiEndpoint: '/api/search', query: 'first' } }
|
|
237
|
-
)
|
|
254
|
+
useBloc(SearchBloc, { staticProps: { query: 'first' } }),
|
|
238
255
|
);
|
|
239
256
|
|
|
240
257
|
await act(async () => {
|
|
241
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
258
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
242
259
|
});
|
|
243
260
|
|
|
244
261
|
expect(result1.current[0].results).toEqual(['Search: first']);
|
|
262
|
+
const instance1Id = result1.current[1]._id;
|
|
245
263
|
|
|
246
|
-
//
|
|
247
|
-
unmount1();
|
|
248
|
-
|
|
249
|
-
// New owner can take over
|
|
264
|
+
// Create second instance with different props
|
|
250
265
|
const { result: result2 } = renderHook(() =>
|
|
251
|
-
useBloc(
|
|
252
|
-
SearchBloc,
|
|
253
|
-
{ props: { apiEndpoint: '/api/search', query: 'second' } }
|
|
254
|
-
)
|
|
266
|
+
useBloc(SearchBloc, { staticProps: { query: 'second' } }),
|
|
255
267
|
);
|
|
256
268
|
|
|
257
269
|
await act(async () => {
|
|
258
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
270
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
259
271
|
});
|
|
260
272
|
|
|
273
|
+
expect(result2.current[0].results).toEqual(['Search: second']);
|
|
274
|
+
const instance2Id = result2.current[1]._id;
|
|
275
|
+
|
|
276
|
+
// Different instances with different auto-generated IDs
|
|
277
|
+
expect(instance1Id).not.toBe(instance2Id);
|
|
278
|
+
expect(result1.current[1]).not.toBe(result2.current[1]);
|
|
279
|
+
|
|
280
|
+
// Unmount first - second is unaffected
|
|
281
|
+
unmount1();
|
|
282
|
+
|
|
261
283
|
expect(result2.current[0].results).toEqual(['Search: second']);
|
|
262
284
|
});
|
|
263
285
|
});
|
|
@@ -265,17 +287,14 @@ describe('useBloc props integration', () => {
|
|
|
265
287
|
describe('Props with other options', () => {
|
|
266
288
|
it('should work with key option', async () => {
|
|
267
289
|
const { result } = renderHook(() =>
|
|
268
|
-
useBloc(
|
|
269
|
-
|
|
270
|
-
{
|
|
271
|
-
|
|
272
|
-
props: { apiEndpoint: '/api/search', query: 'test' }
|
|
273
|
-
}
|
|
274
|
-
)
|
|
290
|
+
useBloc(SearchBloc, {
|
|
291
|
+
instanceId: 'custom-search',
|
|
292
|
+
staticProps: { query: 'test' },
|
|
293
|
+
}),
|
|
275
294
|
);
|
|
276
295
|
|
|
277
296
|
await act(async () => {
|
|
278
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
297
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
279
298
|
});
|
|
280
299
|
|
|
281
300
|
expect(result.current[0].results).toEqual(['Search: test']);
|
|
@@ -287,14 +306,11 @@ describe('useBloc props integration', () => {
|
|
|
287
306
|
const onUnmount = vi.fn();
|
|
288
307
|
|
|
289
308
|
const { result, unmount } = renderHook(() =>
|
|
290
|
-
useBloc(
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
onUnmount
|
|
296
|
-
}
|
|
297
|
-
)
|
|
309
|
+
useBloc(SearchBloc, {
|
|
310
|
+
staticProps: { query: 'test' },
|
|
311
|
+
onMount,
|
|
312
|
+
onUnmount,
|
|
313
|
+
}),
|
|
298
314
|
);
|
|
299
315
|
|
|
300
316
|
expect(onMount).toHaveBeenCalledWith(result.current[1]);
|
|
@@ -306,38 +322,43 @@ describe('useBloc props integration', () => {
|
|
|
306
322
|
|
|
307
323
|
it('should work with manual dependencies', () => {
|
|
308
324
|
const { result, rerender } = renderHook(
|
|
309
|
-
({ step }) =>
|
|
310
|
-
CounterCubit,
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
),
|
|
316
|
-
{ initialProps: { step: 1 } }
|
|
325
|
+
({ step }) =>
|
|
326
|
+
useBloc(CounterCubit, {
|
|
327
|
+
staticProps: { step },
|
|
328
|
+
dependencies: (cubit) => [cubit.state.count],
|
|
329
|
+
}),
|
|
330
|
+
{ initialProps: { step: 1 } },
|
|
317
331
|
);
|
|
318
332
|
|
|
319
|
-
const [, cubit] = result.current;
|
|
333
|
+
const [, cubit] = result.current as [CounterState, CounterCubit];
|
|
320
334
|
|
|
321
335
|
// Increment should trigger re-render (count is a dependency)
|
|
322
336
|
act(() => {
|
|
323
337
|
cubit.increment();
|
|
324
338
|
});
|
|
325
339
|
|
|
326
|
-
expect(result.current[0].count).toBe(1);
|
|
340
|
+
expect((result.current[0] as CounterState).count).toBe(1);
|
|
327
341
|
|
|
328
|
-
// Changing props
|
|
342
|
+
// Changing props creates new instance
|
|
329
343
|
rerender({ step: 2 });
|
|
330
|
-
|
|
344
|
+
|
|
345
|
+
// New instance has different state
|
|
346
|
+
const [newState, newCubit] = result.current as [
|
|
347
|
+
CounterState,
|
|
348
|
+
CounterCubit,
|
|
349
|
+
];
|
|
350
|
+
expect(newCubit).not.toBe(cubit); // New instance
|
|
351
|
+
expect(newState.stepSize).toBe(2);
|
|
352
|
+
expect(newState.count).toBe(0); // New instance starts at 0
|
|
331
353
|
});
|
|
332
354
|
});
|
|
333
355
|
|
|
334
356
|
describe('Edge cases', () => {
|
|
335
357
|
it('should handle undefined props', async () => {
|
|
336
358
|
const { result } = renderHook(() => {
|
|
337
|
-
const [state, cubit] = useBloc(
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
);
|
|
359
|
+
const [state, cubit] = useBloc(CounterCubit, {
|
|
360
|
+
staticProps: undefined,
|
|
361
|
+
});
|
|
341
362
|
// Access count to ensure it's tracked
|
|
342
363
|
void state.count;
|
|
343
364
|
return [state, cubit];
|
|
@@ -345,10 +366,10 @@ describe('useBloc props integration', () => {
|
|
|
345
366
|
|
|
346
367
|
// Wait for any effects
|
|
347
368
|
await act(async () => {
|
|
348
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
369
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
349
370
|
});
|
|
350
371
|
|
|
351
|
-
const [state, cubit] = result.current;
|
|
372
|
+
const [state, cubit] = result.current as [CounterState, CounterCubit];
|
|
352
373
|
expect(state.stepSize).toBe(1);
|
|
353
374
|
|
|
354
375
|
act(() => {
|
|
@@ -357,7 +378,7 @@ describe('useBloc props integration', () => {
|
|
|
357
378
|
|
|
358
379
|
// Wait for React to re-render with new state
|
|
359
380
|
await waitFor(() => {
|
|
360
|
-
expect(result.current[0].count).toBe(1); // Uses default step
|
|
381
|
+
expect((result.current[0] as CounterState).count).toBe(1); // Uses default step
|
|
361
382
|
});
|
|
362
383
|
});
|
|
363
384
|
|
|
@@ -367,12 +388,9 @@ describe('useBloc props integration', () => {
|
|
|
367
388
|
const { rerender } = renderHook(
|
|
368
389
|
({ query }) => {
|
|
369
390
|
renderCount++;
|
|
370
|
-
return useBloc(
|
|
371
|
-
SearchBloc,
|
|
372
|
-
{ props: { apiEndpoint: '/api/search', query } }
|
|
373
|
-
);
|
|
391
|
+
return useBloc(SearchBloc, { staticProps: { query } });
|
|
374
392
|
},
|
|
375
|
-
{ initialProps: { query: 'test' } }
|
|
393
|
+
{ initialProps: { query: 'test' } },
|
|
376
394
|
);
|
|
377
395
|
|
|
378
396
|
const initialRenderCount = renderCount;
|
|
@@ -386,26 +404,30 @@ describe('useBloc props integration', () => {
|
|
|
386
404
|
|
|
387
405
|
it('should handle rapid props updates', async () => {
|
|
388
406
|
const { result, rerender } = renderHook(
|
|
389
|
-
({ query }) => useBloc(
|
|
390
|
-
|
|
391
|
-
{ props: { apiEndpoint: '/api/search', query } }
|
|
392
|
-
),
|
|
393
|
-
{ initialProps: { query: 'initial' } }
|
|
407
|
+
({ query }) => useBloc(SearchBloc, { staticProps: { query } }),
|
|
408
|
+
{ initialProps: { query: 'initial' } },
|
|
394
409
|
);
|
|
395
410
|
|
|
396
411
|
// Wait for initial render
|
|
397
412
|
await act(async () => {
|
|
398
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
413
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
399
414
|
});
|
|
400
415
|
|
|
401
|
-
|
|
416
|
+
const firstInstance = result.current[1];
|
|
417
|
+
|
|
418
|
+
// Rapid updates - each creates a new instance
|
|
402
419
|
await act(async () => {
|
|
403
420
|
rerender({ query: 'update1' });
|
|
404
421
|
rerender({ query: 'update2' });
|
|
405
422
|
rerender({ query: 'update3' });
|
|
406
|
-
await new Promise(resolve => setTimeout(resolve, 50));
|
|
423
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
407
424
|
});
|
|
408
425
|
|
|
426
|
+
const lastInstance = result.current[1];
|
|
427
|
+
|
|
428
|
+
// Different instance after updates
|
|
429
|
+
expect(lastInstance).not.toBe(firstInstance);
|
|
430
|
+
|
|
409
431
|
// Should have the latest value
|
|
410
432
|
expect(result.current[0].results).toEqual(['Search: update3']);
|
|
411
433
|
});
|
|
@@ -115,16 +115,16 @@ describe('useBloc with proxy tracking config', () => {
|
|
|
115
115
|
it('should still respect manual dependencies when provided', () => {
|
|
116
116
|
const renderSpy = vi.fn();
|
|
117
117
|
|
|
118
|
-
const { result } = renderHook(() =>
|
|
118
|
+
const { result } = renderHook(() =>
|
|
119
119
|
useBloc(TestCubit, {
|
|
120
|
-
dependencies: (bloc) => [bloc.state.count]
|
|
121
|
-
})
|
|
120
|
+
dependencies: (bloc) => [bloc.state.count],
|
|
121
|
+
}),
|
|
122
122
|
);
|
|
123
123
|
|
|
124
124
|
// Create another hook that tracks renders
|
|
125
125
|
renderHook(() => {
|
|
126
126
|
const [state] = useBloc(TestCubit, {
|
|
127
|
-
dependencies: (bloc) => [bloc.state.count]
|
|
127
|
+
dependencies: (bloc) => [bloc.state.count],
|
|
128
128
|
});
|
|
129
129
|
renderSpy(state);
|
|
130
130
|
return state;
|
|
@@ -225,4 +225,4 @@ describe('useBloc with proxy tracking config', () => {
|
|
|
225
225
|
expect(renderSpy2).toHaveBeenCalledTimes(2);
|
|
226
226
|
});
|
|
227
227
|
});
|
|
228
|
-
});
|
|
228
|
+
});
|