@isograph/react-disposable-state 0.0.0-main-1cd3db6d → 0.0.0-main-2c275831
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/CacheItem.d.ts +4 -4
- package/dist/CacheItem.js +51 -51
- package/dist/ParentCache.d.ts +2 -2
- package/dist/index.d.ts +8 -8
- package/dist/useCachedPrecommitValue.d.ts +2 -2
- package/dist/useCachedPrecommitValue.js +1 -1
- package/dist/useDisposableState.d.ts +3 -3
- package/dist/useDisposableState.js +5 -7
- package/dist/useHasCommittedRef.d.ts +1 -1
- package/dist/useLazyDisposableState.d.ts +1 -1
- package/dist/useLazyDisposableState.js +3 -3
- package/dist/useUpdatableDisposableState.d.ts +1 -1
- package/dist/useUpdatableDisposableState.js +3 -3
- package/package.json +2 -2
- package/src/CacheItem.test.ts +184 -222
- package/src/CacheItem.ts +64 -81
- package/src/ParentCache.test.ts +9 -10
- package/src/ParentCache.ts +7 -9
- package/src/index.ts +8 -8
- package/src/useCachedPrecommitValue.test.tsx +47 -77
- package/src/useCachedPrecommitValue.ts +9 -13
- package/src/useDisposableState.ts +13 -16
- package/src/useHasCommittedRef.ts +1 -1
- package/src/useLazyDisposableState.ts +7 -7
- package/src/useUpdatableDisposableState.test.tsx +24 -33
- package/src/useUpdatableDisposableState.ts +9 -11
@@ -1,10 +1,10 @@
|
|
1
|
-
import { describe, test, vi, expect, assert } from
|
2
|
-
import { ParentCache } from
|
3
|
-
import { ItemCleanupPair } from
|
4
|
-
import { useCachedPrecommitValue } from
|
5
|
-
import React from
|
6
|
-
import { create } from
|
7
|
-
import { CacheItem, CacheItemState } from
|
1
|
+
import { describe, test, vi, expect, assert } from 'vitest';
|
2
|
+
import { ParentCache } from './ParentCache';
|
3
|
+
import { ItemCleanupPair } from '@isograph/disposable-types';
|
4
|
+
import { useCachedPrecommitValue } from './useCachedPrecommitValue';
|
5
|
+
import React from 'react';
|
6
|
+
import { create } from 'react-test-renderer';
|
7
|
+
import { CacheItem, CacheItemState } from './CacheItem';
|
8
8
|
|
9
9
|
function getItem<T>(cache: ParentCache<T>): CacheItem<T> | null {
|
10
10
|
return (cache as any).__item;
|
@@ -52,26 +52,20 @@ function promiseAndResolver() {
|
|
52
52
|
// The fact that sometimes we need to render in concurrent mode and sometimes
|
53
53
|
// not is a bit worrisome.
|
54
54
|
async function awaitableCreate(Component, isConcurrent) {
|
55
|
-
const element = create(
|
56
|
-
Component,
|
57
|
-
isConcurrent ? { unstable_isConcurrent: true } : undefined
|
58
|
-
);
|
55
|
+
const element = create(Component, isConcurrent ? { unstable_isConcurrent: true } : undefined);
|
59
56
|
await shortPromise();
|
60
57
|
return element;
|
61
58
|
}
|
62
59
|
|
63
|
-
describe(
|
64
|
-
test(
|
60
|
+
describe('useCachedPrecommitValue', () => {
|
61
|
+
test('on initial render, it should call getOrPopulateAndTemporaryRetain', async () => {
|
65
62
|
const disposeItem = vi.fn();
|
66
63
|
const factory = vi.fn(() => {
|
67
64
|
const pair: ItemCleanupPair<number> = [1, disposeItem];
|
68
65
|
return pair;
|
69
66
|
});
|
70
67
|
const cache = new ParentCache(factory);
|
71
|
-
const getOrPopulateAndTemporaryRetain = vi.spyOn(
|
72
|
-
cache,
|
73
|
-
"getOrPopulateAndTemporaryRetain"
|
74
|
-
);
|
68
|
+
const getOrPopulateAndTemporaryRetain = vi.spyOn(cache, 'getOrPopulateAndTemporaryRetain');
|
75
69
|
|
76
70
|
const componentCommits = vi.fn();
|
77
71
|
const hookOnCommit = vi.fn();
|
@@ -103,7 +97,7 @@ describe("useCachedPrecommitValue", () => {
|
|
103
97
|
expect(render).toHaveBeenCalledTimes(1);
|
104
98
|
});
|
105
99
|
|
106
|
-
test(
|
100
|
+
test('on commit, it should call the provided callback and empty the parent cache', async () => {
|
107
101
|
const disposeItem = vi.fn();
|
108
102
|
const factory = vi.fn(() => {
|
109
103
|
const pair: ItemCleanupPair<number> = [1, disposeItem];
|
@@ -124,7 +118,7 @@ describe("useCachedPrecommitValue", () => {
|
|
124
118
|
expect(componentCommits).toHaveBeenCalledTimes(1);
|
125
119
|
expect(hookOnCommit).toBeCalledTimes(1);
|
126
120
|
expect(hookOnCommit.mock.calls[0][0][0]).toBe(1);
|
127
|
-
expect(typeof hookOnCommit.mock.calls[0][0][1]).toBe(
|
121
|
+
expect(typeof hookOnCommit.mock.calls[0][0][1]).toBe('function');
|
128
122
|
expect(factory).toBeCalledTimes(1);
|
129
123
|
expect(disposeItem).not.toBeCalled();
|
130
124
|
expect(cache.isEmpty()).toBe(true);
|
@@ -140,7 +134,7 @@ describe("useCachedPrecommitValue", () => {
|
|
140
134
|
expect(render).toHaveBeenCalledTimes(1);
|
141
135
|
});
|
142
136
|
|
143
|
-
test(
|
137
|
+
test('after commit, on subsequent renders it should return null', async () => {
|
144
138
|
const disposeItem = vi.fn();
|
145
139
|
const factory = vi.fn(() => {
|
146
140
|
const pair: ItemCleanupPair<number> = [1, disposeItem];
|
@@ -186,8 +180,8 @@ describe("useCachedPrecommitValue", () => {
|
|
186
180
|
});
|
187
181
|
|
188
182
|
test(
|
189
|
-
|
190
|
-
|
183
|
+
'on repeated pre-commit renders, if the temporary retain is not disposed, ' +
|
184
|
+
'it should re-call getOrPopulateAndTemporaryRetain but not call factory again',
|
191
185
|
async () => {
|
192
186
|
const disposeItem = vi.fn();
|
193
187
|
const factory = vi.fn(() => {
|
@@ -195,10 +189,7 @@ describe("useCachedPrecommitValue", () => {
|
|
195
189
|
return pair;
|
196
190
|
});
|
197
191
|
const cache = new ParentCache(factory);
|
198
|
-
const getOrPopulateAndTemporaryRetain = vi.spyOn(
|
199
|
-
cache,
|
200
|
-
"getOrPopulateAndTemporaryRetain"
|
201
|
-
);
|
192
|
+
const getOrPopulateAndTemporaryRetain = vi.spyOn(cache, 'getOrPopulateAndTemporaryRetain');
|
202
193
|
|
203
194
|
const componentCommits = vi.fn();
|
204
195
|
const hookOnCommit = vi.fn();
|
@@ -212,9 +203,7 @@ describe("useCachedPrecommitValue", () => {
|
|
212
203
|
expect(factory).toHaveBeenCalledTimes(1);
|
213
204
|
|
214
205
|
renderCount++;
|
215
|
-
expect(getOrPopulateAndTemporaryRetain).toHaveBeenCalledTimes(
|
216
|
-
renderCount
|
217
|
-
);
|
206
|
+
expect(getOrPopulateAndTemporaryRetain).toHaveBeenCalledTimes(renderCount);
|
218
207
|
|
219
208
|
React.useEffect(() => {
|
220
209
|
componentCommits();
|
@@ -233,7 +222,7 @@ describe("useCachedPrecommitValue", () => {
|
|
233
222
|
<TestComponent />
|
234
223
|
<Suspender promise={promise} isResolvedRef={isResolvedRef} />
|
235
224
|
</React.Suspense>,
|
236
|
-
true
|
225
|
+
true,
|
237
226
|
);
|
238
227
|
|
239
228
|
expect(componentCommits).toHaveBeenCalledTimes(0);
|
@@ -244,12 +233,12 @@ describe("useCachedPrecommitValue", () => {
|
|
244
233
|
|
245
234
|
expect(componentCommits).toHaveBeenCalledTimes(1);
|
246
235
|
expect(render).toHaveBeenCalledTimes(2);
|
247
|
-
}
|
236
|
+
},
|
248
237
|
);
|
249
238
|
|
250
239
|
test(
|
251
|
-
|
252
|
-
|
240
|
+
'on repeated pre-commit renders, if the temporary retain is disposed, ' +
|
241
|
+
'it should re-call getOrPopulateAndTemporaryRetain and factory',
|
253
242
|
async () => {
|
254
243
|
const disposeItem = vi.fn();
|
255
244
|
let factoryValue = 0;
|
@@ -260,10 +249,7 @@ describe("useCachedPrecommitValue", () => {
|
|
260
249
|
});
|
261
250
|
const cache = new ParentCache(factory);
|
262
251
|
|
263
|
-
const getOrPopulateAndTemporaryRetain = vi.spyOn(
|
264
|
-
cache,
|
265
|
-
"getOrPopulateAndTemporaryRetain"
|
266
|
-
);
|
252
|
+
const getOrPopulateAndTemporaryRetain = vi.spyOn(cache, 'getOrPopulateAndTemporaryRetain');
|
267
253
|
|
268
254
|
const componentCommits = vi.fn();
|
269
255
|
const hookOnCommit = vi.fn();
|
@@ -305,7 +291,7 @@ describe("useCachedPrecommitValue", () => {
|
|
305
291
|
<TestComponent />
|
306
292
|
<Suspender promise={promise} isResolvedRef={isResolvedRef} />
|
307
293
|
</React.Suspense>,
|
308
|
-
true
|
294
|
+
true,
|
309
295
|
);
|
310
296
|
|
311
297
|
expect(componentCommits).toHaveBeenCalledTimes(0);
|
@@ -316,13 +302,13 @@ describe("useCachedPrecommitValue", () => {
|
|
316
302
|
|
317
303
|
expect(componentCommits).toHaveBeenCalledTimes(1);
|
318
304
|
expect(render).toHaveBeenCalledTimes(2);
|
319
|
-
}
|
305
|
+
},
|
320
306
|
);
|
321
307
|
|
322
308
|
test(
|
323
|
-
|
324
|
-
|
325
|
-
|
309
|
+
'if the item has been disposed between the render and the commit, ' +
|
310
|
+
'and the parent cache is empty, it will call factory again, re-render an ' +
|
311
|
+
'additional time and called onCommit with the newly generated item',
|
326
312
|
async () => {
|
327
313
|
const disposeItem = vi.fn();
|
328
314
|
let factoryCount = 0;
|
@@ -332,14 +318,8 @@ describe("useCachedPrecommitValue", () => {
|
|
332
318
|
return pair;
|
333
319
|
});
|
334
320
|
const cache = new ParentCache(factory);
|
335
|
-
const getOrPopulateAndTemporaryRetain = vi.spyOn(
|
336
|
-
|
337
|
-
"getOrPopulateAndTemporaryRetain"
|
338
|
-
);
|
339
|
-
const getAndPermanentRetainIfPresent = vi.spyOn(
|
340
|
-
cache,
|
341
|
-
"getAndPermanentRetainIfPresent"
|
342
|
-
);
|
321
|
+
const getOrPopulateAndTemporaryRetain = vi.spyOn(cache, 'getOrPopulateAndTemporaryRetain');
|
322
|
+
const getAndPermanentRetainIfPresent = vi.spyOn(cache, 'getAndPermanentRetainIfPresent');
|
343
323
|
|
344
324
|
const componentCommits = vi.fn();
|
345
325
|
const hookOnCommit = vi.fn();
|
@@ -353,9 +333,7 @@ describe("useCachedPrecommitValue", () => {
|
|
353
333
|
componentCommits();
|
354
334
|
expect(getOrPopulateAndTemporaryRetain).toHaveBeenCalledTimes(1);
|
355
335
|
expect(getAndPermanentRetainIfPresent).toHaveBeenCalledTimes(1);
|
356
|
-
expect(getAndPermanentRetainIfPresent.mock.results[0].value).toBe(
|
357
|
-
null
|
358
|
-
);
|
336
|
+
expect(getAndPermanentRetainIfPresent.mock.results[0].value).toBe(null);
|
359
337
|
expect(factory).toHaveBeenCalledTimes(2);
|
360
338
|
expect(cache.isEmpty()).toBe(true);
|
361
339
|
expect(hookOnCommit).toHaveBeenCalledTimes(1);
|
@@ -402,20 +380,20 @@ describe("useCachedPrecommitValue", () => {
|
|
402
380
|
<TestComponent />
|
403
381
|
<CodeExecutor />
|
404
382
|
</>,
|
405
|
-
false
|
383
|
+
false,
|
406
384
|
);
|
407
385
|
|
408
386
|
// This code executes after the commit and re-render of TestComponent.
|
409
387
|
// The commit triggers a re-render, because the item was disposed.
|
410
388
|
expect(render).toHaveBeenCalledTimes(2);
|
411
389
|
expect(factory).toBeCalledTimes(2);
|
412
|
-
}
|
390
|
+
},
|
413
391
|
);
|
414
392
|
|
415
393
|
test(
|
416
|
-
|
417
|
-
|
418
|
-
|
394
|
+
'if, between the render and the commit, the item has been disposed, ' +
|
395
|
+
'and the parent cache is not empty, it will not call factory again, will re-render ' +
|
396
|
+
'an additional time and will call onCommit with the value in the parent cache',
|
419
397
|
async () => {
|
420
398
|
const disposeItem = vi.fn();
|
421
399
|
let factoryCount = 0;
|
@@ -425,10 +403,7 @@ describe("useCachedPrecommitValue", () => {
|
|
425
403
|
return pair;
|
426
404
|
});
|
427
405
|
const cache = new ParentCache(factory);
|
428
|
-
const getAndPermanentRetainIfPresent = vi.spyOn(
|
429
|
-
cache,
|
430
|
-
"getAndPermanentRetainIfPresent"
|
431
|
-
);
|
406
|
+
const getAndPermanentRetainIfPresent = vi.spyOn(cache, 'getAndPermanentRetainIfPresent');
|
432
407
|
|
433
408
|
const componentCommits = vi.fn();
|
434
409
|
const hookOnCommit = vi.fn();
|
@@ -442,9 +417,7 @@ describe("useCachedPrecommitValue", () => {
|
|
442
417
|
// Note that we called getOrPopulateAndTemporaryRetain during CodeExecutor, hence 2
|
443
418
|
expect(getOrPopulateAndTemporaryRetain).toHaveBeenCalledTimes(2);
|
444
419
|
expect(getAndPermanentRetainIfPresent).toHaveBeenCalledTimes(1);
|
445
|
-
expect(getAndPermanentRetainIfPresent.mock.results[0].value[0]).toBe(
|
446
|
-
2
|
447
|
-
);
|
420
|
+
expect(getAndPermanentRetainIfPresent.mock.results[0].value[0]).toBe(2);
|
448
421
|
expect(factory).toHaveBeenCalledTimes(2);
|
449
422
|
expect(hookOnCommit).toHaveBeenCalledTimes(1);
|
450
423
|
expect(hookOnCommit.mock.calls[0][0][0]).toBe(2);
|
@@ -453,10 +426,7 @@ describe("useCachedPrecommitValue", () => {
|
|
453
426
|
return <div />;
|
454
427
|
}
|
455
428
|
|
456
|
-
const getOrPopulateAndTemporaryRetain = vi.spyOn(
|
457
|
-
cache,
|
458
|
-
"getOrPopulateAndTemporaryRetain"
|
459
|
-
);
|
429
|
+
const getOrPopulateAndTemporaryRetain = vi.spyOn(cache, 'getOrPopulateAndTemporaryRetain');
|
460
430
|
|
461
431
|
// wat is going on?
|
462
432
|
//
|
@@ -499,7 +469,7 @@ describe("useCachedPrecommitValue", () => {
|
|
499
469
|
<TestComponent />
|
500
470
|
<CodeExecutor />
|
501
471
|
</React.Suspense>,
|
502
|
-
false
|
472
|
+
false,
|
503
473
|
);
|
504
474
|
|
505
475
|
// This code executes after the commit and re-render of TestComponent.
|
@@ -508,13 +478,13 @@ describe("useCachedPrecommitValue", () => {
|
|
508
478
|
// Note that this is the same number of calls as inside of CodeExecutor,
|
509
479
|
// implying that the factory function was not called again.
|
510
480
|
expect(factory).toBeCalledTimes(2);
|
511
|
-
}
|
481
|
+
},
|
512
482
|
);
|
513
483
|
|
514
484
|
test(
|
515
|
-
|
516
|
-
|
517
|
-
|
485
|
+
'If the component unmounts before committing, ' +
|
486
|
+
'the item will remain in the parent cache, ' +
|
487
|
+
'temporarily retained',
|
518
488
|
async () => {
|
519
489
|
const disposeItem = vi.fn();
|
520
490
|
const factory = vi.fn(() => {
|
@@ -570,7 +540,7 @@ describe("useCachedPrecommitValue", () => {
|
|
570
540
|
// If we're not in concurrent mode, TestComponent will mount before
|
571
541
|
// unmounting. This perhaps is a bug in react-test-renderer. Regardless,
|
572
542
|
// we're not interested in that scenario.
|
573
|
-
true
|
543
|
+
true,
|
574
544
|
);
|
575
545
|
|
576
546
|
// This code executes after the commit and re-render of TestComponent.
|
@@ -579,9 +549,9 @@ describe("useCachedPrecommitValue", () => {
|
|
579
549
|
expect(componentCommits).toHaveBeenCalledTimes(0);
|
580
550
|
const item = getItem(cache)!;
|
581
551
|
const state = getState(item);
|
582
|
-
assert(state.kind ===
|
552
|
+
assert(state.kind === 'InParentCacheAndNotDisposed');
|
583
553
|
expect(state.permanentRetainCount).toBe(0);
|
584
554
|
expect(state.temporaryRetainCount).toBe(1);
|
585
|
-
}
|
555
|
+
},
|
586
556
|
);
|
587
557
|
});
|
@@ -1,9 +1,9 @@
|
|
1
|
-
|
1
|
+
'use strict';
|
2
2
|
|
3
|
-
import { useEffect, useState } from
|
4
|
-
import { ParentCache } from
|
5
|
-
import { useHasCommittedRef } from
|
6
|
-
import { ItemCleanupPair } from
|
3
|
+
import { useEffect, useState } from 'react';
|
4
|
+
import { ParentCache } from './ParentCache';
|
5
|
+
import { useHasCommittedRef } from './useHasCommittedRef';
|
6
|
+
import { ItemCleanupPair } from '@isograph/isograph-disposable-types/dist';
|
7
7
|
|
8
8
|
/**
|
9
9
|
* usePrecommitValue<T>
|
@@ -38,7 +38,7 @@ import { ItemCleanupPair } from "@isograph/isograph-disposable-types/dist";
|
|
38
38
|
*/
|
39
39
|
export function useCachedPrecommitValue<T>(
|
40
40
|
parentCache: ParentCache<T>,
|
41
|
-
onCommit: (pair: ItemCleanupPair<T>) => void
|
41
|
+
onCommit: (pair: ItemCleanupPair<T>) => void,
|
42
42
|
): { state: T } | null {
|
43
43
|
// TODO: there should be two APIs. One in which we always re-render if the
|
44
44
|
// committed item was not returned during the last render, and one in which
|
@@ -65,16 +65,13 @@ export function useCachedPrecommitValue<T>(
|
|
65
65
|
//
|
66
66
|
// After the above, we have a non-disposed item and a cleanup function, which we
|
67
67
|
// can pass to onCommit.
|
68
|
-
const undisposedPair = cacheItem.permanentRetainIfNotDisposed(
|
69
|
-
disposeOfTemporaryRetain
|
70
|
-
);
|
68
|
+
const undisposedPair = cacheItem.permanentRetainIfNotDisposed(disposeOfTemporaryRetain);
|
71
69
|
if (undisposedPair !== null) {
|
72
70
|
onCommit(undisposedPair);
|
73
71
|
} else {
|
74
72
|
// The cache item we created during render has been disposed. Check if the parent
|
75
73
|
// cache is populated.
|
76
|
-
const existingCacheItemCleanupPair =
|
77
|
-
parentCache.getAndPermanentRetainIfPresent();
|
74
|
+
const existingCacheItemCleanupPair = parentCache.getAndPermanentRetainIfPresent();
|
78
75
|
if (existingCacheItemCleanupPair !== null) {
|
79
76
|
onCommit(existingCacheItemCleanupPair);
|
80
77
|
} else {
|
@@ -97,8 +94,7 @@ export function useCachedPrecommitValue<T>(
|
|
97
94
|
|
98
95
|
// Safety: item is only safe to use (i.e. guaranteed not to have disposed)
|
99
96
|
// during this tick.
|
100
|
-
const [cacheItem, item, disposeOfTemporaryRetain] =
|
101
|
-
parentCache.getOrPopulateAndTemporaryRetain();
|
97
|
+
const [cacheItem, item, disposeOfTemporaryRetain] = parentCache.getOrPopulateAndTemporaryRetain();
|
102
98
|
|
103
99
|
return { state: item };
|
104
100
|
}
|
@@ -1,12 +1,12 @@
|
|
1
|
-
import { useEffect, useRef } from
|
2
|
-
import { ParentCache } from
|
3
|
-
import { ItemCleanupPair } from
|
4
|
-
import { useCachedPrecommitValue } from
|
1
|
+
import { useEffect, useRef } from 'react';
|
2
|
+
import { ParentCache } from './ParentCache';
|
3
|
+
import { ItemCleanupPair } from '@isograph/disposable-types';
|
4
|
+
import { useCachedPrecommitValue } from './useCachedPrecommitValue';
|
5
5
|
import {
|
6
6
|
UNASSIGNED_STATE,
|
7
7
|
UnassignedState,
|
8
8
|
useUpdatableDisposableState,
|
9
|
-
} from
|
9
|
+
} from './useUpdatableDisposableState';
|
10
10
|
|
11
11
|
type UseUpdatableDisposableStateReturnValue<T> = {
|
12
12
|
state: T;
|
@@ -14,7 +14,7 @@ type UseUpdatableDisposableStateReturnValue<T> = {
|
|
14
14
|
};
|
15
15
|
|
16
16
|
export function useDisposableState<T = never>(
|
17
|
-
parentCache: ParentCache<T
|
17
|
+
parentCache: ParentCache<T>,
|
18
18
|
): UseUpdatableDisposableStateReturnValue<T> {
|
19
19
|
const itemCleanupPairRef = useRef<ItemCleanupPair<T> | null>(null);
|
20
20
|
|
@@ -22,8 +22,7 @@ export function useDisposableState<T = never>(
|
|
22
22
|
itemCleanupPairRef.current = pair;
|
23
23
|
});
|
24
24
|
|
25
|
-
const { state: stateFromDisposableStateHook, setState } =
|
26
|
-
useUpdatableDisposableState<T>();
|
25
|
+
const { state: stateFromDisposableStateHook, setState } = useUpdatableDisposableState<T>();
|
27
26
|
|
28
27
|
useEffect(
|
29
28
|
function cleanupItemCleanupPairRefAfterSetState() {
|
@@ -33,13 +32,13 @@ export function useDisposableState<T = never>(
|
|
33
32
|
itemCleanupPairRef.current = null;
|
34
33
|
} else {
|
35
34
|
throw new Error(
|
36
|
-
|
37
|
-
|
35
|
+
'itemCleanupPairRef.current is unexpectedly null. ' +
|
36
|
+
'This indicates a bug in react-disposable-state.',
|
38
37
|
);
|
39
38
|
}
|
40
39
|
}
|
41
40
|
},
|
42
|
-
[stateFromDisposableStateHook]
|
41
|
+
[stateFromDisposableStateHook],
|
43
42
|
);
|
44
43
|
|
45
44
|
useEffect(function cleanupItemCleanupPairRefIfSetStateNotCalled() {
|
@@ -65,9 +64,7 @@ export function useDisposableState<T = never>(
|
|
65
64
|
// can still be assigned, during the render before the
|
66
65
|
// cleanupItemCleanupPairRefAfterSetState effect is called.
|
67
66
|
const state: T | undefined =
|
68
|
-
(stateFromDisposableStateHook != UNASSIGNED_STATE
|
69
|
-
? stateFromDisposableStateHook
|
70
|
-
: null) ??
|
67
|
+
(stateFromDisposableStateHook != UNASSIGNED_STATE ? stateFromDisposableStateHook : null) ??
|
71
68
|
itemCleanupPairRef.current?.[0] ??
|
72
69
|
preCommitItem?.state;
|
73
70
|
|
@@ -82,11 +79,11 @@ function tsTests() {
|
|
82
79
|
let x: any;
|
83
80
|
const a = useDisposableState(x);
|
84
81
|
// @ts-expect-error
|
85
|
-
a.setState([
|
82
|
+
a.setState(['asdf', () => {}]);
|
86
83
|
// @ts-expect-error
|
87
84
|
a.setState([UNASSIGNED_STATE, () => {}]);
|
88
85
|
const b = useDisposableState<string | UnassignedState>(x);
|
89
86
|
// @ts-expect-error
|
90
87
|
b.setState([UNASSIGNED_STATE, () => {}]);
|
91
|
-
b.setState([
|
88
|
+
b.setState(['asdf', () => {}]);
|
92
89
|
}
|
@@ -1,9 +1,9 @@
|
|
1
|
-
|
1
|
+
'use strict';
|
2
2
|
|
3
|
-
import { useEffect, useRef } from
|
4
|
-
import type { ItemCleanupPair } from
|
5
|
-
import { ParentCache } from
|
6
|
-
import { useCachedPrecommitValue } from
|
3
|
+
import { useEffect, useRef } from 'react';
|
4
|
+
import type { ItemCleanupPair } from '@isograph/disposable-types';
|
5
|
+
import { ParentCache } from './ParentCache';
|
6
|
+
import { useCachedPrecommitValue } from './useCachedPrecommitValue';
|
7
7
|
|
8
8
|
/**
|
9
9
|
* useLazyDisposableState<T>
|
@@ -28,7 +28,7 @@ export function useLazyDisposableState<T>(parentCache: ParentCache<T>): {
|
|
28
28
|
// TODO confirm useEffect is called in order.
|
29
29
|
if (cleanupFn == null) {
|
30
30
|
throw new Error(
|
31
|
-
|
31
|
+
'cleanupFn unexpectedly null. This indicates a bug in react-disposable-state.',
|
32
32
|
);
|
33
33
|
}
|
34
34
|
return cleanupFn;
|
@@ -43,6 +43,6 @@ export function useLazyDisposableState<T>(parentCache: ParentCache<T>): {
|
|
43
43
|
// is non-null. During the initial commit, we assign itemCleanupPairRef.current,
|
44
44
|
// so during subsequent renders, itemCleanupPairRef.current is non-null.
|
45
45
|
throw new Error(
|
46
|
-
|
46
|
+
'returnedItem was unexpectedly null. This indicates a bug in react-disposable-state.',
|
47
47
|
);
|
48
48
|
}
|
@@ -1,10 +1,7 @@
|
|
1
|
-
import { describe, test, vi, expect } from
|
2
|
-
import React from
|
3
|
-
import { create } from
|
4
|
-
import {
|
5
|
-
useUpdatableDisposableState,
|
6
|
-
UNASSIGNED_STATE,
|
7
|
-
} from "./useUpdatableDisposableState";
|
1
|
+
import { describe, test, vi, expect } from 'vitest';
|
2
|
+
import React from 'react';
|
3
|
+
import { create } from 'react-test-renderer';
|
4
|
+
import { useUpdatableDisposableState, UNASSIGNED_STATE } from './useUpdatableDisposableState';
|
8
5
|
|
9
6
|
function Suspender({ promise, isResolvedRef }) {
|
10
7
|
if (!isResolvedRef.current) {
|
@@ -44,29 +41,26 @@ function promiseAndResolver() {
|
|
44
41
|
// The fact that sometimes we need to render in concurrent mode and sometimes
|
45
42
|
// not is a bit worrisome.
|
46
43
|
async function awaitableCreate(Component, isConcurrent) {
|
47
|
-
const element = create(
|
48
|
-
Component,
|
49
|
-
isConcurrent ? { unstable_isConcurrent: true } : undefined
|
50
|
-
);
|
44
|
+
const element = create(Component, isConcurrent ? { unstable_isConcurrent: true } : undefined);
|
51
45
|
await shortPromise();
|
52
46
|
return element;
|
53
47
|
}
|
54
48
|
|
55
|
-
describe(
|
56
|
-
test(
|
49
|
+
describe('useUpdatableDisposableState', () => {
|
50
|
+
test('it should return a sentinel value initially and a setter', async () => {
|
57
51
|
const render = vi.fn();
|
58
52
|
function TestComponent() {
|
59
53
|
render();
|
60
54
|
const value = useUpdatableDisposableState();
|
61
55
|
expect(value.state).toBe(UNASSIGNED_STATE);
|
62
|
-
expect(typeof value.setState).toBe(
|
56
|
+
expect(typeof value.setState).toBe('function');
|
63
57
|
return null;
|
64
58
|
}
|
65
59
|
await awaitableCreate(<TestComponent />, false);
|
66
60
|
expect(render).toHaveBeenCalledTimes(1);
|
67
61
|
});
|
68
62
|
|
69
|
-
test(
|
63
|
+
test('it should allow you to update the value in state', async () => {
|
70
64
|
const render = vi.fn();
|
71
65
|
let value;
|
72
66
|
function TestComponent() {
|
@@ -85,7 +79,7 @@ describe("useUpdatableDisposableState", () => {
|
|
85
79
|
expect(value.state).toEqual(1);
|
86
80
|
});
|
87
81
|
|
88
|
-
test(
|
82
|
+
test('it should dispose previous values on commit', async () => {
|
89
83
|
const render = vi.fn();
|
90
84
|
const componentCommits = vi.fn();
|
91
85
|
let value;
|
@@ -120,7 +114,7 @@ describe("useUpdatableDisposableState", () => {
|
|
120
114
|
expect(componentCommits).toHaveBeenCalled();
|
121
115
|
});
|
122
116
|
|
123
|
-
test(
|
117
|
+
test('it should dispose identical previous values on commit', async () => {
|
124
118
|
const render = vi.fn();
|
125
119
|
const componentCommits = vi.fn();
|
126
120
|
let value;
|
@@ -158,7 +152,7 @@ describe("useUpdatableDisposableState", () => {
|
|
158
152
|
expect(componentCommits).toHaveBeenCalled();
|
159
153
|
});
|
160
154
|
|
161
|
-
test(
|
155
|
+
test('it should dispose multiple previous values on commit', async () => {
|
162
156
|
const render = vi.fn();
|
163
157
|
const componentCommits = vi.fn();
|
164
158
|
let value;
|
@@ -202,7 +196,7 @@ describe("useUpdatableDisposableState", () => {
|
|
202
196
|
expect(componentCommits).toHaveBeenCalled();
|
203
197
|
});
|
204
198
|
|
205
|
-
test(
|
199
|
+
test('it should throw if setState is called during a render before commit', async () => {
|
206
200
|
let didCatch;
|
207
201
|
function TestComponent() {
|
208
202
|
const value = useUpdatableDisposableState<number>();
|
@@ -219,7 +213,7 @@ describe("useUpdatableDisposableState", () => {
|
|
219
213
|
expect(didCatch).toBe(true);
|
220
214
|
});
|
221
215
|
|
222
|
-
test(
|
216
|
+
test('it should not throw if setState is called during render after commit', async () => {
|
223
217
|
let value;
|
224
218
|
const cleanupFn = vi.fn();
|
225
219
|
const sawCorrectValue = vi.fn();
|
@@ -254,7 +248,7 @@ describe("useUpdatableDisposableState", () => {
|
|
254
248
|
expect(value.state).toBe(1);
|
255
249
|
});
|
256
250
|
|
257
|
-
test(
|
251
|
+
test('it should throw if setState is called after a render before commit', async () => {
|
258
252
|
let value;
|
259
253
|
const componentCommits = vi.fn();
|
260
254
|
function TestComponent() {
|
@@ -271,7 +265,7 @@ describe("useUpdatableDisposableState", () => {
|
|
271
265
|
<TestComponent />
|
272
266
|
<Suspender promise={promise} isResolvedRef={isResolvedRef} />
|
273
267
|
</React.Suspense>,
|
274
|
-
true
|
268
|
+
true,
|
275
269
|
);
|
276
270
|
|
277
271
|
expect(componentCommits).not.toHaveBeenCalled();
|
@@ -282,8 +276,8 @@ describe("useUpdatableDisposableState", () => {
|
|
282
276
|
});
|
283
277
|
|
284
278
|
test(
|
285
|
-
|
286
|
-
|
279
|
+
'it should dispose items that were set during ' +
|
280
|
+
'suspense when the component commits due to unsuspense',
|
287
281
|
async () => {
|
288
282
|
// Note that "during suspense" implies that there is no commit, so this
|
289
283
|
// follows from the descriptions of the previous tests. Nonetheless, we
|
@@ -320,7 +314,7 @@ describe("useUpdatableDisposableState", () => {
|
|
320
314
|
<React.Suspense fallback="fallback">
|
321
315
|
<ParentComponent />
|
322
316
|
</React.Suspense>,
|
323
|
-
true
|
317
|
+
true,
|
324
318
|
);
|
325
319
|
|
326
320
|
expect(render).toHaveBeenCalledTimes(1);
|
@@ -354,10 +348,10 @@ describe("useUpdatableDisposableState", () => {
|
|
354
348
|
expect(cleanup1).toHaveBeenCalledTimes(1);
|
355
349
|
expect(render).toHaveBeenCalledTimes(3);
|
356
350
|
expect(componentCommits).toHaveBeenCalledTimes(2);
|
357
|
-
}
|
351
|
+
},
|
358
352
|
);
|
359
353
|
|
360
|
-
test(
|
354
|
+
test('it should properly clean up all items passed to setState during suspense on unmount', async () => {
|
361
355
|
let value;
|
362
356
|
const componentCommits = vi.fn();
|
363
357
|
const render = vi.fn();
|
@@ -391,7 +385,7 @@ describe("useUpdatableDisposableState", () => {
|
|
391
385
|
<React.Suspense fallback="fallback">
|
392
386
|
<ParentComponent shouldMountRef={shouldMountRef} />
|
393
387
|
</React.Suspense>,
|
394
|
-
true
|
388
|
+
true,
|
395
389
|
);
|
396
390
|
|
397
391
|
expect(render).toHaveBeenCalledTimes(1);
|
@@ -430,7 +424,7 @@ describe("useUpdatableDisposableState", () => {
|
|
430
424
|
expect(cleanup2).toHaveBeenCalled();
|
431
425
|
});
|
432
426
|
|
433
|
-
test(
|
427
|
+
test('it should clean up the item currently in state on unmount', async () => {
|
434
428
|
let value;
|
435
429
|
const componentCommits = vi.fn();
|
436
430
|
const render = vi.fn();
|
@@ -452,10 +446,7 @@ describe("useUpdatableDisposableState", () => {
|
|
452
446
|
|
453
447
|
const shouldMountRef = { current: true };
|
454
448
|
|
455
|
-
await awaitableCreate(
|
456
|
-
<ParentComponent shouldMountRef={shouldMountRef} />,
|
457
|
-
true
|
458
|
-
);
|
449
|
+
await awaitableCreate(<ParentComponent shouldMountRef={shouldMountRef} />, true);
|
459
450
|
|
460
451
|
expect(render).toHaveBeenCalledTimes(1);
|
461
452
|
expect(componentCommits).toHaveBeenCalledTimes(1);
|