@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
@@ -0,0 +1,70 @@
|
|
1
|
+
import { ItemCleanupPair } from '@isograph/disposable-types';
|
2
|
+
import React, { useEffect, useState } from 'react';
|
3
|
+
import { create } from 'react-test-renderer';
|
4
|
+
import { describe, expect, test, vi } from 'vitest';
|
5
|
+
import { ParentCache } from './ParentCache';
|
6
|
+
import { useLazyDisposableState } from './useLazyDisposableState';
|
7
|
+
function createCache<T>(value: T) {
|
8
|
+
const disposeItem = vi.fn();
|
9
|
+
const factory = vi.fn(() => {
|
10
|
+
const pair: ItemCleanupPair<T> = [value, disposeItem];
|
11
|
+
return pair;
|
12
|
+
});
|
13
|
+
const cache = new ParentCache(factory);
|
14
|
+
return { cache, disposeItem };
|
15
|
+
}
|
16
|
+
|
17
|
+
function promiseWithResolvers() {
|
18
|
+
let resolve;
|
19
|
+
let reject;
|
20
|
+
const promise = new Promise((_resolve, _reject) => {
|
21
|
+
resolve = _resolve;
|
22
|
+
reject = _reject;
|
23
|
+
});
|
24
|
+
return { resolve, reject, promise };
|
25
|
+
}
|
26
|
+
|
27
|
+
describe('useLazyDisposableState', async () => {
|
28
|
+
test('on cache change, it should dispose previous cache', async () => {
|
29
|
+
const cache1 = createCache(1);
|
30
|
+
const cache2 = createCache(2);
|
31
|
+
const renders = vi.fn();
|
32
|
+
|
33
|
+
let unmounted = promiseWithResolvers();
|
34
|
+
let committed = promiseWithResolvers();
|
35
|
+
|
36
|
+
function TestComponent() {
|
37
|
+
const [cache, setCache] = useState(cache1.cache);
|
38
|
+
const { state } = useLazyDisposableState(cache);
|
39
|
+
|
40
|
+
useEffect(() => {
|
41
|
+
setCache(cache2.cache);
|
42
|
+
|
43
|
+
return () => {
|
44
|
+
unmounted.resolve();
|
45
|
+
};
|
46
|
+
}, []);
|
47
|
+
|
48
|
+
useEffect(() => {
|
49
|
+
if (state == 1) return;
|
50
|
+
committed.resolve();
|
51
|
+
}, [state]);
|
52
|
+
|
53
|
+
renders(state);
|
54
|
+
|
55
|
+
return null;
|
56
|
+
}
|
57
|
+
|
58
|
+
const root = create(<TestComponent />, { unstable_isConcurrent: true });
|
59
|
+
await committed.promise;
|
60
|
+
expect(cache1.disposeItem).toHaveBeenCalled();
|
61
|
+
expect(cache1.cache.factory).toHaveBeenCalledOnce();
|
62
|
+
root.unmount();
|
63
|
+
await unmounted.promise;
|
64
|
+
expect(cache2.disposeItem).toHaveBeenCalled();
|
65
|
+
expect(cache2.cache.factory).toHaveBeenCalledOnce();
|
66
|
+
expect(renders).toHaveBeenNthCalledWith(1, 1);
|
67
|
+
expect(renders).toHaveBeenNthCalledWith(2, 2);
|
68
|
+
expect(renders).toHaveBeenCalledTimes(2);
|
69
|
+
});
|
70
|
+
});
|
@@ -1,40 +1,52 @@
|
|
1
1
|
'use strict';
|
2
2
|
|
3
3
|
import { useEffect, useRef } from 'react';
|
4
|
-
|
4
|
+
|
5
|
+
import type { ItemCleanupPair } from '@isograph/isograph-disposable-types';
|
5
6
|
import { ParentCache } from './ParentCache';
|
6
|
-
|
7
|
+
|
8
|
+
import { type UnassignedState } from './useUpdatableDisposableState';
|
9
|
+
import { useCachedResponsivePrecommitValue } from './useCachedResponsivePrecommitValue';
|
7
10
|
|
8
11
|
/**
|
9
12
|
* useLazyDisposableState<T>
|
10
13
|
* - Takes a mutable parent cache and a factory function
|
11
14
|
* - Returns { state: T }
|
12
15
|
*
|
13
|
-
* This lazily loads the disposable item using
|
16
|
+
* This lazily loads the disposable item using useCachedResponsivePrecommitValue, then
|
14
17
|
* (on commit) sets it in state. The item continues to be returned after
|
15
18
|
* commit and is disposed when the hook unmounts.
|
16
19
|
*/
|
17
|
-
export function useLazyDisposableState<T>(
|
20
|
+
export function useLazyDisposableState<T>(
|
21
|
+
parentCache: ParentCache<Exclude<T, UnassignedState>>,
|
22
|
+
): {
|
18
23
|
state: T;
|
19
24
|
} {
|
20
25
|
const itemCleanupPairRef = useRef<ItemCleanupPair<T> | null>(null);
|
21
26
|
|
22
|
-
const preCommitItem =
|
23
|
-
|
24
|
-
|
27
|
+
const preCommitItem = useCachedResponsivePrecommitValue(
|
28
|
+
parentCache,
|
29
|
+
(pair) => {
|
30
|
+
itemCleanupPairRef.current?.[1]();
|
31
|
+
itemCleanupPairRef.current = pair;
|
32
|
+
},
|
33
|
+
);
|
25
34
|
|
26
35
|
useEffect(() => {
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
36
|
+
return () => {
|
37
|
+
const cleanupFn = itemCleanupPairRef.current?.[1];
|
38
|
+
// TODO confirm useEffect is called in order.
|
39
|
+
if (cleanupFn == null) {
|
40
|
+
throw new Error(
|
41
|
+
'cleanupFn unexpectedly null. This indicates a bug in react-disposable-state.',
|
42
|
+
);
|
43
|
+
}
|
44
|
+
return cleanupFn();
|
45
|
+
};
|
35
46
|
}, []);
|
36
47
|
|
37
48
|
const returnedItem = preCommitItem?.state ?? itemCleanupPairRef.current?.[0];
|
49
|
+
|
38
50
|
if (returnedItem != null) {
|
39
51
|
return { state: returnedItem };
|
40
52
|
}
|
@@ -2,7 +2,7 @@ import { ItemCleanupPair } from '@isograph/disposable-types';
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
3
3
|
import { useHasCommittedRef } from './useHasCommittedRef';
|
4
4
|
|
5
|
-
export const UNASSIGNED_STATE = Symbol();
|
5
|
+
export const UNASSIGNED_STATE: unique symbol = Symbol();
|
6
6
|
export type UnassignedState = typeof UNASSIGNED_STATE;
|
7
7
|
|
8
8
|
type UseUpdatableDisposableStateReturnValue<T> = {
|