@isograph/react-disposable-state 0.1.1 → 0.3.0
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 +1 -0
- package/dist/CacheItem.d.ts.map +1 -0
- package/dist/CacheItem.js +2 -2
- package/dist/ParentCache.d.ts +2 -1
- package/dist/ParentCache.d.ts.map +1 -0
- package/dist/ParentCache.js +3 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -1
- package/dist/{useCachedPrecommitValue.d.ts → useCachedResponsivePrecommitValue.d.ts} +12 -9
- package/dist/useCachedResponsivePrecommitValue.d.ts.map +1 -0
- package/dist/{useCachedPrecommitValue.js → useCachedResponsivePrecommitValue.js} +17 -16
- package/dist/useDisposableState.d.ts +2 -1
- package/dist/useDisposableState.d.ts.map +1 -0
- package/dist/useDisposableState.js +7 -6
- package/dist/useHasCommittedRef.d.ts +1 -0
- package/dist/useHasCommittedRef.d.ts.map +1 -0
- package/dist/useHasCommittedRef.js +1 -2
- package/dist/useLazyDisposableState.d.ts +4 -2
- package/dist/useLazyDisposableState.d.ts.map +1 -0
- package/dist/useLazyDisposableState.js +15 -12
- package/dist/useUpdatableDisposableState.d.ts +1 -0
- package/dist/useUpdatableDisposableState.d.ts.map +1 -0
- package/dist/useUpdatableDisposableState.js +2 -2
- package/package.json +11 -8
- package/src/ParentCache.ts +3 -1
- package/src/index.ts +1 -1
- package/src/useCachedResponsivePrecommitValue.test.tsx +571 -0
- package/src/{useCachedPrecommitValue.ts → useCachedResponsivePrecommitValue.ts} +18 -17
- package/src/useDisposableState.ts +12 -7
- package/src/useLazyDisposableState.test.tsx +70 -0
- package/src/useLazyDisposableState.ts +27 -15
- package/src/useUpdatableDisposableState.ts +1 -1
- package/tsconfig.json +6 -0
- package/src/useCachedPrecommitValue.test.tsx +0 -577
@@ -1,577 +0,0 @@
|
|
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
|
-
|
9
|
-
function getItem<T>(cache: ParentCache<T>): CacheItem<T> | null {
|
10
|
-
return (cache as any).__cacheItem;
|
11
|
-
}
|
12
|
-
|
13
|
-
function getState<T>(cacheItem: CacheItem<T>): CacheItemState<T> {
|
14
|
-
return (cacheItem as any).__state;
|
15
|
-
}
|
16
|
-
|
17
|
-
function Suspender({ promise, isResolvedRef }) {
|
18
|
-
if (!isResolvedRef.current) {
|
19
|
-
throw promise;
|
20
|
-
}
|
21
|
-
return null;
|
22
|
-
}
|
23
|
-
|
24
|
-
function shortPromise() {
|
25
|
-
let resolve;
|
26
|
-
const promise = new Promise((_resolve) => {
|
27
|
-
resolve = _resolve;
|
28
|
-
});
|
29
|
-
|
30
|
-
setTimeout(resolve, 1);
|
31
|
-
return promise;
|
32
|
-
}
|
33
|
-
|
34
|
-
function promiseAndResolver() {
|
35
|
-
let resolve;
|
36
|
-
const isResolvedRef = {
|
37
|
-
current: false,
|
38
|
-
};
|
39
|
-
const promise = new Promise((r) => {
|
40
|
-
resolve = r;
|
41
|
-
});
|
42
|
-
return {
|
43
|
-
promise,
|
44
|
-
resolve: () => {
|
45
|
-
isResolvedRef.current = true;
|
46
|
-
resolve();
|
47
|
-
},
|
48
|
-
isResolvedRef,
|
49
|
-
};
|
50
|
-
}
|
51
|
-
|
52
|
-
// The fact that sometimes we need to render in concurrent mode and sometimes
|
53
|
-
// not is a bit worrisome.
|
54
|
-
async function awaitableCreate(Component, isConcurrent) {
|
55
|
-
const element = create(
|
56
|
-
Component,
|
57
|
-
isConcurrent ? { unstable_isConcurrent: true } : undefined,
|
58
|
-
);
|
59
|
-
await shortPromise();
|
60
|
-
return element;
|
61
|
-
}
|
62
|
-
|
63
|
-
describe('fake describe block to make test pass', () => {
|
64
|
-
test('foo', () => {});
|
65
|
-
});
|
66
|
-
|
67
|
-
if (false) {
|
68
|
-
describe('useCachedPrecommitValue', () => {
|
69
|
-
test('on initial render, it should call getOrPopulateAndTemporaryRetain', async () => {
|
70
|
-
const disposeItem = vi.fn();
|
71
|
-
const factory = vi.fn(() => {
|
72
|
-
const pair: ItemCleanupPair<number> = [1, disposeItem];
|
73
|
-
return pair;
|
74
|
-
});
|
75
|
-
const cache = new ParentCache(factory);
|
76
|
-
const getOrPopulateAndTemporaryRetain = vi.spyOn(
|
77
|
-
cache,
|
78
|
-
'getOrPopulateAndTemporaryRetain',
|
79
|
-
);
|
80
|
-
|
81
|
-
const componentCommits = vi.fn();
|
82
|
-
const hookOnCommit = vi.fn();
|
83
|
-
const render = vi.fn();
|
84
|
-
function TestComponent() {
|
85
|
-
render();
|
86
|
-
React.useEffect(componentCommits);
|
87
|
-
|
88
|
-
const data = useCachedPrecommitValue(cache, hookOnCommit);
|
89
|
-
|
90
|
-
expect(render).toBeCalledTimes(1);
|
91
|
-
expect(componentCommits).not.toBeCalled();
|
92
|
-
expect(hookOnCommit).not.toBeCalled();
|
93
|
-
expect(data).toEqual({ state: 1 });
|
94
|
-
expect(factory).toBeCalledTimes(1);
|
95
|
-
expect(disposeItem).not.toBeCalled();
|
96
|
-
expect(getOrPopulateAndTemporaryRetain).toBeCalledTimes(1);
|
97
|
-
|
98
|
-
// TODO we should assert that permanentRetainIfNotDisposed was called
|
99
|
-
// on the cache item.
|
100
|
-
|
101
|
-
return <div />;
|
102
|
-
}
|
103
|
-
|
104
|
-
await awaitableCreate(<TestComponent />, false);
|
105
|
-
|
106
|
-
expect(componentCommits).toBeCalledTimes(1);
|
107
|
-
expect(hookOnCommit).toBeCalledTimes(1);
|
108
|
-
expect(render).toHaveBeenCalledTimes(1);
|
109
|
-
});
|
110
|
-
|
111
|
-
test('on commit, it should call the provided callback and empty the parent cache', async () => {
|
112
|
-
const disposeItem = vi.fn();
|
113
|
-
const factory = vi.fn(() => {
|
114
|
-
const pair: ItemCleanupPair<number> = [1, disposeItem];
|
115
|
-
return pair;
|
116
|
-
});
|
117
|
-
const cache = new ParentCache(factory);
|
118
|
-
|
119
|
-
const componentCommits = vi.fn();
|
120
|
-
const hookOnCommit = vi.fn();
|
121
|
-
const render = vi.fn();
|
122
|
-
function TestComponent() {
|
123
|
-
render();
|
124
|
-
expect(render).toHaveBeenCalledTimes(1);
|
125
|
-
const data = useCachedPrecommitValue(cache, hookOnCommit);
|
126
|
-
|
127
|
-
React.useEffect(() => {
|
128
|
-
componentCommits();
|
129
|
-
expect(componentCommits).toHaveBeenCalledTimes(1);
|
130
|
-
expect(hookOnCommit).toBeCalledTimes(1);
|
131
|
-
expect(hookOnCommit.mock.calls[0][0][0]).toBe(1);
|
132
|
-
expect(typeof hookOnCommit.mock.calls[0][0][1]).toBe('function');
|
133
|
-
expect(factory).toBeCalledTimes(1);
|
134
|
-
expect(disposeItem).not.toBeCalled();
|
135
|
-
expect(cache.isEmpty()).toBe(true);
|
136
|
-
}, []);
|
137
|
-
|
138
|
-
expect(factory).toBeCalledTimes(1);
|
139
|
-
expect(disposeItem).not.toBeCalled();
|
140
|
-
return <div />;
|
141
|
-
}
|
142
|
-
|
143
|
-
await awaitableCreate(<TestComponent />, false);
|
144
|
-
expect(componentCommits).toBeCalledTimes(1);
|
145
|
-
expect(render).toHaveBeenCalledTimes(1);
|
146
|
-
});
|
147
|
-
|
148
|
-
test('after commit, on subsequent renders it should return null', async () => {
|
149
|
-
const disposeItem = vi.fn();
|
150
|
-
const factory = vi.fn(() => {
|
151
|
-
const pair: ItemCleanupPair<number> = [1, disposeItem];
|
152
|
-
return pair;
|
153
|
-
});
|
154
|
-
const cache = new ParentCache(factory);
|
155
|
-
|
156
|
-
const componentCommits = vi.fn();
|
157
|
-
const hookOnCommit = vi.fn();
|
158
|
-
let setState;
|
159
|
-
let initialRender = true;
|
160
|
-
function TestComponent() {
|
161
|
-
const [, _setState] = React.useState(null);
|
162
|
-
setState = _setState;
|
163
|
-
const value = useCachedPrecommitValue(cache, hookOnCommit);
|
164
|
-
|
165
|
-
if (initialRender && value !== null) {
|
166
|
-
initialRender = false;
|
167
|
-
expect(value).toEqual({ state: 1 });
|
168
|
-
} else {
|
169
|
-
expect(value).toEqual(null);
|
170
|
-
}
|
171
|
-
|
172
|
-
React.useEffect(() => {
|
173
|
-
componentCommits();
|
174
|
-
expect(componentCommits).toHaveBeenCalledTimes(1);
|
175
|
-
expect(hookOnCommit).toBeCalledTimes(1);
|
176
|
-
expect(factory).toBeCalledTimes(1);
|
177
|
-
expect(disposeItem).not.toBeCalled();
|
178
|
-
}, []);
|
179
|
-
|
180
|
-
return <div />;
|
181
|
-
}
|
182
|
-
|
183
|
-
await awaitableCreate(<TestComponent />, false);
|
184
|
-
|
185
|
-
expect(componentCommits).toHaveBeenCalledTimes(1);
|
186
|
-
|
187
|
-
// Trigger a re-render
|
188
|
-
setState({});
|
189
|
-
await shortPromise();
|
190
|
-
expect(initialRender).toBe(false);
|
191
|
-
});
|
192
|
-
|
193
|
-
test(
|
194
|
-
'on repeated pre-commit renders, if the temporary retain is not disposed, ' +
|
195
|
-
'it should re-call getOrPopulateAndTemporaryRetain but not call factory again',
|
196
|
-
async () => {
|
197
|
-
const disposeItem = vi.fn();
|
198
|
-
const factory = vi.fn(() => {
|
199
|
-
const pair: ItemCleanupPair<number> = [1, disposeItem];
|
200
|
-
return pair;
|
201
|
-
});
|
202
|
-
const cache = new ParentCache(factory);
|
203
|
-
const getOrPopulateAndTemporaryRetain = vi.spyOn(
|
204
|
-
cache,
|
205
|
-
'getOrPopulateAndTemporaryRetain',
|
206
|
-
);
|
207
|
-
|
208
|
-
const componentCommits = vi.fn();
|
209
|
-
const hookOnCommit = vi.fn();
|
210
|
-
const render = vi.fn();
|
211
|
-
let renderCount = 0;
|
212
|
-
function TestComponent() {
|
213
|
-
render();
|
214
|
-
const value = useCachedPrecommitValue(cache, hookOnCommit);
|
215
|
-
|
216
|
-
expect(value).toEqual({ state: 1 });
|
217
|
-
expect(factory).toHaveBeenCalledTimes(1);
|
218
|
-
|
219
|
-
renderCount++;
|
220
|
-
expect(getOrPopulateAndTemporaryRetain).toHaveBeenCalledTimes(
|
221
|
-
renderCount,
|
222
|
-
);
|
223
|
-
|
224
|
-
React.useEffect(() => {
|
225
|
-
componentCommits();
|
226
|
-
expect(componentCommits).toHaveBeenCalledTimes(1);
|
227
|
-
expect(hookOnCommit).toBeCalledTimes(1);
|
228
|
-
expect(factory).toBeCalledTimes(1);
|
229
|
-
expect(disposeItem).not.toBeCalled();
|
230
|
-
}, []);
|
231
|
-
|
232
|
-
return <div />;
|
233
|
-
}
|
234
|
-
|
235
|
-
const { promise, isResolvedRef, resolve } = promiseAndResolver();
|
236
|
-
await awaitableCreate(
|
237
|
-
<React.Suspense fallback={<div />}>
|
238
|
-
<TestComponent />
|
239
|
-
<Suspender promise={promise} isResolvedRef={isResolvedRef} />
|
240
|
-
</React.Suspense>,
|
241
|
-
true,
|
242
|
-
);
|
243
|
-
|
244
|
-
expect(componentCommits).toHaveBeenCalledTimes(0);
|
245
|
-
expect(render).toHaveBeenCalledTimes(1);
|
246
|
-
|
247
|
-
resolve();
|
248
|
-
await shortPromise();
|
249
|
-
|
250
|
-
expect(componentCommits).toHaveBeenCalledTimes(1);
|
251
|
-
expect(render).toHaveBeenCalledTimes(2);
|
252
|
-
},
|
253
|
-
);
|
254
|
-
|
255
|
-
test(
|
256
|
-
'on repeated pre-commit renders, if the temporary retain is disposed, ' +
|
257
|
-
'it should re-call getOrPopulateAndTemporaryRetain and factory',
|
258
|
-
async () => {
|
259
|
-
const disposeItem = vi.fn();
|
260
|
-
let factoryValue = 0;
|
261
|
-
const factory = vi.fn(() => {
|
262
|
-
factoryValue++;
|
263
|
-
const pair: ItemCleanupPair<number> = [factoryValue, disposeItem];
|
264
|
-
return pair;
|
265
|
-
});
|
266
|
-
const cache = new ParentCache(factory);
|
267
|
-
|
268
|
-
const getOrPopulateAndTemporaryRetain = vi.spyOn(
|
269
|
-
cache,
|
270
|
-
'getOrPopulateAndTemporaryRetain',
|
271
|
-
);
|
272
|
-
|
273
|
-
const componentCommits = vi.fn();
|
274
|
-
const hookOnCommit = vi.fn();
|
275
|
-
const render = vi.fn();
|
276
|
-
function TestComponent() {
|
277
|
-
render();
|
278
|
-
const value = useCachedPrecommitValue(cache, hookOnCommit);
|
279
|
-
|
280
|
-
expect(value).toEqual({ state: factoryValue });
|
281
|
-
|
282
|
-
React.useEffect(() => {
|
283
|
-
componentCommits();
|
284
|
-
expect(cache.isEmpty()).toBe(true);
|
285
|
-
expect(componentCommits).toHaveBeenCalledTimes(1);
|
286
|
-
expect(hookOnCommit).toBeCalledTimes(1);
|
287
|
-
expect(hookOnCommit.mock.calls[0][0][0]).toBe(2);
|
288
|
-
expect(factory).toBeCalledTimes(2);
|
289
|
-
expect(disposeItem).toBeCalledTimes(1);
|
290
|
-
}, []);
|
291
|
-
|
292
|
-
if (render.mock.calls.length === 1) {
|
293
|
-
expect(factory).toHaveBeenCalledTimes(1);
|
294
|
-
// First render, dispose the temporary retain
|
295
|
-
expect(disposeItem).toBeCalledTimes(0);
|
296
|
-
getOrPopulateAndTemporaryRetain.mock.results[0].value[2]();
|
297
|
-
expect(disposeItem).toBeCalledTimes(1);
|
298
|
-
expect(getOrPopulateAndTemporaryRetain).toHaveBeenCalledTimes(1);
|
299
|
-
} else {
|
300
|
-
expect(factory).toHaveBeenCalledTimes(2);
|
301
|
-
expect(getOrPopulateAndTemporaryRetain).toHaveBeenCalledTimes(2);
|
302
|
-
}
|
303
|
-
|
304
|
-
return <div />;
|
305
|
-
}
|
306
|
-
|
307
|
-
const { promise, isResolvedRef, resolve } = promiseAndResolver();
|
308
|
-
await awaitableCreate(
|
309
|
-
<React.Suspense fallback={<div />}>
|
310
|
-
<TestComponent />
|
311
|
-
<Suspender promise={promise} isResolvedRef={isResolvedRef} />
|
312
|
-
</React.Suspense>,
|
313
|
-
true,
|
314
|
-
);
|
315
|
-
|
316
|
-
expect(componentCommits).toHaveBeenCalledTimes(0);
|
317
|
-
expect(render).toHaveBeenCalledTimes(1);
|
318
|
-
|
319
|
-
resolve();
|
320
|
-
await shortPromise();
|
321
|
-
|
322
|
-
expect(componentCommits).toHaveBeenCalledTimes(1);
|
323
|
-
expect(render).toHaveBeenCalledTimes(2);
|
324
|
-
},
|
325
|
-
);
|
326
|
-
|
327
|
-
test(
|
328
|
-
'if the item has been disposed between the render and the commit, ' +
|
329
|
-
'and the parent cache is empty, it will call factory again, re-render an ' +
|
330
|
-
'additional time and called onCommit with the newly generated item',
|
331
|
-
async () => {
|
332
|
-
const disposeItem = vi.fn();
|
333
|
-
let factoryCount = 0;
|
334
|
-
const factory = vi.fn(() => {
|
335
|
-
factoryCount++;
|
336
|
-
const pair: ItemCleanupPair<number> = [factoryCount, disposeItem];
|
337
|
-
return pair;
|
338
|
-
});
|
339
|
-
const cache = new ParentCache(factory);
|
340
|
-
const getOrPopulateAndTemporaryRetain = vi.spyOn(
|
341
|
-
cache,
|
342
|
-
'getOrPopulateAndTemporaryRetain',
|
343
|
-
);
|
344
|
-
const getAndPermanentRetainIfPresent = vi.spyOn(
|
345
|
-
cache,
|
346
|
-
'getAndPermanentRetainIfPresent',
|
347
|
-
);
|
348
|
-
|
349
|
-
const componentCommits = vi.fn();
|
350
|
-
const hookOnCommit = vi.fn();
|
351
|
-
const render = vi.fn();
|
352
|
-
function TestComponent() {
|
353
|
-
render();
|
354
|
-
|
355
|
-
useCachedPrecommitValue(cache, hookOnCommit);
|
356
|
-
|
357
|
-
React.useEffect(() => {
|
358
|
-
componentCommits();
|
359
|
-
expect(getOrPopulateAndTemporaryRetain).toHaveBeenCalledTimes(1);
|
360
|
-
expect(getAndPermanentRetainIfPresent).toHaveBeenCalledTimes(1);
|
361
|
-
expect(getAndPermanentRetainIfPresent.mock.results[0].value).toBe(
|
362
|
-
null,
|
363
|
-
);
|
364
|
-
expect(factory).toHaveBeenCalledTimes(2);
|
365
|
-
expect(cache.isEmpty()).toBe(true);
|
366
|
-
expect(hookOnCommit).toHaveBeenCalledTimes(1);
|
367
|
-
expect(hookOnCommit.mock.calls[0][0][0]).toBe(2);
|
368
|
-
}, []);
|
369
|
-
|
370
|
-
return <div />;
|
371
|
-
}
|
372
|
-
|
373
|
-
// wat is going on?
|
374
|
-
//
|
375
|
-
// We want to test a scenario where the item is disposed between the render and
|
376
|
-
// the commit.
|
377
|
-
//
|
378
|
-
// The subcomponents are rendered in order: TestComponent followed by CodeExecutor.
|
379
|
-
//
|
380
|
-
// - During TestComponent's render, it will populate the cache.
|
381
|
-
// - Then, CodeExecutor will render, and dispose the temporary retain,
|
382
|
-
// disposing the cache item. The parent cache will be empty as well.
|
383
|
-
// - Then, TestComponent commits.
|
384
|
-
let initialRender = true;
|
385
|
-
function CodeExecutor() {
|
386
|
-
if (initialRender) {
|
387
|
-
// This code executes after the initial render of TestComponent, but before
|
388
|
-
// it commits.
|
389
|
-
expect(disposeItem).not.toHaveBeenCalled();
|
390
|
-
getOrPopulateAndTemporaryRetain.mock.results[0].value[2]();
|
391
|
-
expect(disposeItem).toHaveBeenCalledTimes(1);
|
392
|
-
expect(cache.isEmpty()).toBe(true);
|
393
|
-
|
394
|
-
expect(render).toHaveBeenCalledTimes(1);
|
395
|
-
expect(hookOnCommit).toBeCalledTimes(0);
|
396
|
-
expect(componentCommits).toBeCalledTimes(0);
|
397
|
-
expect(factory).toHaveBeenCalledTimes(1);
|
398
|
-
|
399
|
-
initialRender = false;
|
400
|
-
}
|
401
|
-
|
402
|
-
return null;
|
403
|
-
}
|
404
|
-
|
405
|
-
const element = await awaitableCreate(
|
406
|
-
<>
|
407
|
-
<TestComponent />
|
408
|
-
<CodeExecutor />
|
409
|
-
</>,
|
410
|
-
false,
|
411
|
-
);
|
412
|
-
|
413
|
-
// This code executes after the commit and re-render of TestComponent.
|
414
|
-
// The commit triggers a re-render, because the item was disposed.
|
415
|
-
expect(render).toHaveBeenCalledTimes(2);
|
416
|
-
expect(factory).toBeCalledTimes(2);
|
417
|
-
},
|
418
|
-
);
|
419
|
-
|
420
|
-
test(
|
421
|
-
'if, between the render and the commit, the item has been disposed, ' +
|
422
|
-
'and the parent cache is not empty, it will not call factory again, will re-render ' +
|
423
|
-
'an additional time and will call onCommit with the value in the parent cache',
|
424
|
-
async () => {
|
425
|
-
const disposeItem = vi.fn();
|
426
|
-
let factoryCount = 0;
|
427
|
-
const factory = vi.fn(() => {
|
428
|
-
factoryCount++;
|
429
|
-
const pair: ItemCleanupPair<number> = [factoryCount, disposeItem];
|
430
|
-
return pair;
|
431
|
-
});
|
432
|
-
const cache = new ParentCache(factory);
|
433
|
-
const getAndPermanentRetainIfPresent = vi.spyOn(
|
434
|
-
cache,
|
435
|
-
'getAndPermanentRetainIfPresent',
|
436
|
-
);
|
437
|
-
|
438
|
-
const componentCommits = vi.fn();
|
439
|
-
const hookOnCommit = vi.fn();
|
440
|
-
const render = vi.fn();
|
441
|
-
function TestComponent() {
|
442
|
-
render();
|
443
|
-
useCachedPrecommitValue(cache, hookOnCommit);
|
444
|
-
|
445
|
-
React.useEffect(() => {
|
446
|
-
componentCommits();
|
447
|
-
// Note that we called getOrPopulateAndTemporaryRetain during CodeExecutor, hence 2
|
448
|
-
expect(getOrPopulateAndTemporaryRetain).toHaveBeenCalledTimes(2);
|
449
|
-
expect(getAndPermanentRetainIfPresent).toHaveBeenCalledTimes(1);
|
450
|
-
expect(
|
451
|
-
getAndPermanentRetainIfPresent.mock.results[0].value[0],
|
452
|
-
).toBe(2);
|
453
|
-
expect(factory).toHaveBeenCalledTimes(2);
|
454
|
-
expect(hookOnCommit).toHaveBeenCalledTimes(1);
|
455
|
-
expect(hookOnCommit.mock.calls[0][0][0]).toBe(2);
|
456
|
-
}, []);
|
457
|
-
|
458
|
-
return <div />;
|
459
|
-
}
|
460
|
-
|
461
|
-
const getOrPopulateAndTemporaryRetain = vi.spyOn(
|
462
|
-
cache,
|
463
|
-
'getOrPopulateAndTemporaryRetain',
|
464
|
-
);
|
465
|
-
|
466
|
-
// wat is going on?
|
467
|
-
//
|
468
|
-
// We want to test a scenario where the item is disposed between the render and
|
469
|
-
// the commit.
|
470
|
-
//
|
471
|
-
// The subcomponents are rendered in order: TestComponent followed by CodeExecutor.
|
472
|
-
//
|
473
|
-
// - During TestComponent's render, it will populate the cache.
|
474
|
-
// - Then, CodeExecutor will render, and dispose the temporary retain,
|
475
|
-
// disposing the cache item. It will then repopulate the parent cache.
|
476
|
-
// - Then, TestComponent commits.
|
477
|
-
let initialRender = true;
|
478
|
-
function CodeExecutor() {
|
479
|
-
if (initialRender) {
|
480
|
-
// This code executes after the initial render of TestComponent, but before
|
481
|
-
// it commits.
|
482
|
-
expect(disposeItem).not.toHaveBeenCalled();
|
483
|
-
getOrPopulateAndTemporaryRetain.mock.results[0].value[2]();
|
484
|
-
expect(disposeItem).toHaveBeenCalledTimes(1);
|
485
|
-
expect(cache.isEmpty()).toBe(true);
|
486
|
-
|
487
|
-
cache.getOrPopulateAndTemporaryRetain();
|
488
|
-
expect(cache.isEmpty()).toBe(false);
|
489
|
-
// The factory function was called when we called getOrPopulateAndTemporaryRetain
|
490
|
-
expect(factory).toHaveBeenCalledTimes(2);
|
491
|
-
|
492
|
-
expect(render).toHaveBeenCalledTimes(1);
|
493
|
-
expect(hookOnCommit).toBeCalledTimes(0);
|
494
|
-
expect(componentCommits).toBeCalledTimes(0);
|
495
|
-
|
496
|
-
initialRender = false;
|
497
|
-
}
|
498
|
-
|
499
|
-
return null;
|
500
|
-
}
|
501
|
-
|
502
|
-
const element = await awaitableCreate(
|
503
|
-
<React.Suspense fallback="fallback">
|
504
|
-
<TestComponent />
|
505
|
-
<CodeExecutor />
|
506
|
-
</React.Suspense>,
|
507
|
-
false,
|
508
|
-
);
|
509
|
-
|
510
|
-
// This code executes after the commit and re-render of TestComponent.
|
511
|
-
// The commit triggers a re-render, because the item was disposed.
|
512
|
-
expect(render).toHaveBeenCalledTimes(2);
|
513
|
-
// Note that this is the same number of calls as inside of CodeExecutor,
|
514
|
-
// implying that the factory function was not called again.
|
515
|
-
expect(factory).toBeCalledTimes(2);
|
516
|
-
},
|
517
|
-
);
|
518
|
-
|
519
|
-
test(
|
520
|
-
'After render but before commit, the item will ' +
|
521
|
-
'be in the parent cache, temporarily retained',
|
522
|
-
async () => {
|
523
|
-
const disposeItem = vi.fn();
|
524
|
-
const factory = vi.fn(() => {
|
525
|
-
const pair: ItemCleanupPair<number> = [1, disposeItem];
|
526
|
-
return pair;
|
527
|
-
});
|
528
|
-
const cache = new ParentCache(factory);
|
529
|
-
|
530
|
-
const componentCommits = vi.fn();
|
531
|
-
const hookOnCommit = vi.fn();
|
532
|
-
const render = vi.fn();
|
533
|
-
function TestComponent() {
|
534
|
-
render();
|
535
|
-
|
536
|
-
useCachedPrecommitValue(cache, hookOnCommit);
|
537
|
-
|
538
|
-
React.useEffect(() => {
|
539
|
-
componentCommits();
|
540
|
-
}, []);
|
541
|
-
|
542
|
-
return <div />;
|
543
|
-
}
|
544
|
-
|
545
|
-
// wat is going on?
|
546
|
-
//
|
547
|
-
// We want to test a scenario where the component unmounts before committing.
|
548
|
-
// However, we cannot distinguish between an unmount before commit and a
|
549
|
-
// render and a commit that hasn't happened yet.
|
550
|
-
//
|
551
|
-
// This can be simulated with suspense.
|
552
|
-
//
|
553
|
-
// This test and 'on initial render, it should call getOrPopulateAndTemporaryRetain'
|
554
|
-
// can be merged
|
555
|
-
|
556
|
-
const { promise, isResolvedRef } = promiseAndResolver();
|
557
|
-
const element = await awaitableCreate(
|
558
|
-
<React.Suspense fallback={null}>
|
559
|
-
<TestComponent />
|
560
|
-
<Suspender promise={promise} isResolvedRef={isResolvedRef} />
|
561
|
-
</React.Suspense>,
|
562
|
-
true,
|
563
|
-
);
|
564
|
-
|
565
|
-
// This code executes after the commit and re-render of TestComponent.
|
566
|
-
// The commit triggers a re-render, because the item was disposed.
|
567
|
-
expect(render).toHaveBeenCalledTimes(1);
|
568
|
-
expect(componentCommits).toHaveBeenCalledTimes(0);
|
569
|
-
const item = getItem(cache)!;
|
570
|
-
const state = getState(item);
|
571
|
-
assert(state.kind === 'InParentCacheAndNotDisposed');
|
572
|
-
expect(state.permanentRetainCount).toBe(0);
|
573
|
-
expect(state.temporaryRetainCount).toBe(1);
|
574
|
-
},
|
575
|
-
);
|
576
|
-
});
|
577
|
-
}
|