@isograph/react-disposable-state 0.5.0 → 0.5.1
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/.turbo/turbo-compile-libs.log +9 -2
- package/dist/CacheItem.d.mts +63 -0
- package/dist/CacheItem.d.mts.map +1 -0
- package/dist/CacheItem.d.ts +33 -29
- package/dist/CacheItem.d.ts.map +1 -1
- package/dist/CacheItem.js +191 -266
- package/dist/CacheItem.mjs +193 -0
- package/dist/CacheItem.mjs.map +1 -0
- package/dist/ParentCache.d.mts +45 -0
- package/dist/ParentCache.d.mts.map +1 -0
- package/dist/ParentCache.d.ts +27 -22
- package/dist/ParentCache.d.ts.map +1 -1
- package/dist/ParentCache.js +69 -85
- package/dist/ParentCache.mjs +73 -0
- package/dist/ParentCache.mjs.map +1 -0
- package/dist/_virtual/rolldown_runtime.js +25 -0
- package/dist/index.d.mts +9 -0
- package/dist/index.d.ts +9 -9
- package/dist/index.js +24 -24
- package/dist/index.mjs +11 -0
- package/dist/useCachedResponsivePrecommitValue.d.mts +43 -0
- package/dist/useCachedResponsivePrecommitValue.d.mts.map +1 -0
- package/dist/useCachedResponsivePrecommitValue.d.ts +9 -4
- package/dist/useCachedResponsivePrecommitValue.d.ts.map +1 -1
- package/dist/useCachedResponsivePrecommitValue.js +55 -90
- package/dist/useCachedResponsivePrecommitValue.mjs +57 -0
- package/dist/useCachedResponsivePrecommitValue.mjs.map +1 -0
- package/dist/useDisposableState.d.mts +13 -0
- package/dist/useDisposableState.d.mts.map +1 -0
- package/dist/useDisposableState.d.ts +10 -7
- package/dist/useDisposableState.d.ts.map +1 -1
- package/dist/useDisposableState.js +36 -69
- package/dist/useDisposableState.mjs +37 -0
- package/dist/useDisposableState.mjs.map +1 -0
- package/dist/useHasCommittedRef.d.mts +11 -0
- package/dist/useHasCommittedRef.d.mts.map +1 -0
- package/dist/useHasCommittedRef.d.ts +7 -2
- package/dist/useHasCommittedRef.d.ts.map +1 -1
- package/dist/useHasCommittedRef.js +15 -11
- package/dist/useHasCommittedRef.mjs +17 -0
- package/dist/useHasCommittedRef.mjs.map +1 -0
- package/dist/useLazyDisposableState.d.mts +20 -0
- package/dist/useLazyDisposableState.d.mts.map +1 -0
- package/dist/useLazyDisposableState.d.ts +9 -4
- package/dist/useLazyDisposableState.d.ts.map +1 -1
- package/dist/useLazyDisposableState.js +32 -39
- package/dist/useLazyDisposableState.mjs +34 -0
- package/dist/useLazyDisposableState.mjs.map +1 -0
- package/dist/useUpdatableDisposableState.d.mts +43 -0
- package/dist/useUpdatableDisposableState.d.mts.map +1 -0
- package/dist/useUpdatableDisposableState.d.ts +10 -7
- package/dist/useUpdatableDisposableState.d.ts.map +1 -1
- package/dist/useUpdatableDisposableState.js +73 -89
- package/dist/useUpdatableDisposableState.mjs +74 -0
- package/dist/useUpdatableDisposableState.mjs.map +1 -0
- package/package.json +14 -6
- package/src/CacheItem.test.ts +4 -7
- package/src/CacheItem.ts +16 -11
- package/src/ParentCache.test.ts +5 -5
- package/src/ParentCache.ts +5 -4
- package/src/useCachedResponsivePrecommitValue.test.tsx +15 -14
- package/src/useCachedResponsivePrecommitValue.ts +4 -4
- package/src/useDisposableState.ts +21 -16
- package/src/useHasCommittedRef.ts +1 -1
- package/src/useLazyDisposableState.test.tsx +2 -2
- package/src/useLazyDisposableState.ts +1 -1
- package/src/useUpdatableDisposableState.test.tsx +19 -5
- package/src/useUpdatableDisposableState.ts +1 -1
- package/dist/index.d.ts.map +0 -1
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import { MutableRefObject } from
|
|
1
|
+
import { MutableRefObject } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/useHasCommittedRef.d.ts
|
|
4
|
+
|
|
2
5
|
/**
|
|
3
6
|
* Returns true if the component has committed, false otherwise.
|
|
4
7
|
*/
|
|
5
|
-
|
|
8
|
+
declare function useHasCommittedRef(): MutableRefObject<boolean>;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { useHasCommittedRef };
|
|
6
11
|
//# sourceMappingURL=useHasCommittedRef.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHasCommittedRef.d.ts","
|
|
1
|
+
{"version":3,"file":"useHasCommittedRef.d.ts","names":[],"sources":["../src/useHasCommittedRef.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;iBAAgB,kBAAA,CAAA,GAAsB"}
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.js');
|
|
2
|
+
let react = require("react");
|
|
3
|
+
react = require_rolldown_runtime.__toESM(react);
|
|
4
|
+
|
|
5
|
+
//#region src/useHasCommittedRef.ts
|
|
5
6
|
/**
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
* Returns true if the component has committed, false otherwise.
|
|
8
|
+
*/
|
|
8
9
|
function useHasCommittedRef() {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
const hasCommittedRef = (0, react.useRef)(false);
|
|
11
|
+
(0, react.useEffect)(() => {
|
|
12
|
+
hasCommittedRef.current = true;
|
|
13
|
+
}, []);
|
|
14
|
+
return hasCommittedRef;
|
|
14
15
|
}
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
exports.useHasCommittedRef = useHasCommittedRef;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/useHasCommittedRef.ts
|
|
4
|
+
/**
|
|
5
|
+
* Returns true if the component has committed, false otherwise.
|
|
6
|
+
*/
|
|
7
|
+
function useHasCommittedRef() {
|
|
8
|
+
const hasCommittedRef = useRef(false);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
hasCommittedRef.current = true;
|
|
11
|
+
}, []);
|
|
12
|
+
return hasCommittedRef;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
16
|
+
export { useHasCommittedRef };
|
|
17
|
+
//# sourceMappingURL=useHasCommittedRef.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useHasCommittedRef.mjs","names":[],"sources":["../src/useHasCommittedRef.ts"],"sourcesContent":["import { type MutableRefObject, useEffect, useRef } from 'react';\n\n/**\n * Returns true if the component has committed, false otherwise.\n */\nexport function useHasCommittedRef(): MutableRefObject<boolean> {\n const hasCommittedRef = useRef(false);\n useEffect(() => {\n hasCommittedRef.current = true;\n }, []);\n return hasCommittedRef;\n}\n"],"mappings":";;;;;;AAKA,SAAgB,qBAAgD;CAC9D,MAAM,kBAAkB,OAAO,MAAM;AACrC,iBAAgB;AACd,kBAAgB,UAAU;IACzB,EAAE,CAAC;AACN,QAAO"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ParentCache } from "./ParentCache.mjs";
|
|
2
|
+
import { UnassignedState } from "./useUpdatableDisposableState.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/useLazyDisposableState.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* useLazyDisposableState<T>
|
|
8
|
+
* - Takes a mutable parent cache and a factory function
|
|
9
|
+
* - Returns { state: T }
|
|
10
|
+
*
|
|
11
|
+
* This lazily loads the disposable item using useCachedResponsivePrecommitValue, then
|
|
12
|
+
* (on commit) sets it in state. The item continues to be returned after
|
|
13
|
+
* commit and is disposed when the hook unmounts.
|
|
14
|
+
*/
|
|
15
|
+
declare function useLazyDisposableState<T>(parentCache: ParentCache<Exclude<T, UnassignedState>>): {
|
|
16
|
+
state: T;
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
export { useLazyDisposableState };
|
|
20
|
+
//# sourceMappingURL=useLazyDisposableState.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLazyDisposableState.d.mts","names":[],"sources":["../src/useLazyDisposableState.ts"],"sourcesContent":[],"mappings":";;;;;;;AAiBA;;;;;;;iBAAgB,uCACD,YAAY,QAAQ,GAAG;SAE7B"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { ParentCache } from
|
|
2
|
-
import {
|
|
1
|
+
import { ParentCache } from "./ParentCache.js";
|
|
2
|
+
import { UnassignedState } from "./useUpdatableDisposableState.js";
|
|
3
|
+
|
|
4
|
+
//#region src/useLazyDisposableState.d.ts
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
7
|
* useLazyDisposableState<T>
|
|
5
8
|
* - Takes a mutable parent cache and a factory function
|
|
@@ -9,7 +12,9 @@ import { type UnassignedState } from './useUpdatableDisposableState';
|
|
|
9
12
|
* (on commit) sets it in state. The item continues to be returned after
|
|
10
13
|
* commit and is disposed when the hook unmounts.
|
|
11
14
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
declare function useLazyDisposableState<T>(parentCache: ParentCache<Exclude<T, UnassignedState>>): {
|
|
16
|
+
state: T;
|
|
14
17
|
};
|
|
18
|
+
//#endregion
|
|
19
|
+
export { useLazyDisposableState };
|
|
15
20
|
//# sourceMappingURL=useLazyDisposableState.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLazyDisposableState.d.ts","
|
|
1
|
+
{"version":3,"file":"useLazyDisposableState.d.ts","names":[],"sources":["../src/useLazyDisposableState.ts"],"sourcesContent":[],"mappings":";;;;;;;AAiBA;;;;;;;iBAAgB,uCACD,YAAY,QAAQ,GAAG;SAE7B"}
|
|
@@ -1,42 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.js');
|
|
2
|
+
const require_useCachedResponsivePrecommitValue = require('./useCachedResponsivePrecommitValue.js');
|
|
3
|
+
let react = require("react");
|
|
4
|
+
react = require_rolldown_runtime.__toESM(react);
|
|
5
|
+
|
|
6
|
+
//#region src/useLazyDisposableState.ts
|
|
6
7
|
/**
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
* useLazyDisposableState<T>
|
|
9
|
+
* - Takes a mutable parent cache and a factory function
|
|
10
|
+
* - Returns { state: T }
|
|
11
|
+
*
|
|
12
|
+
* This lazily loads the disposable item using useCachedResponsivePrecommitValue, then
|
|
13
|
+
* (on commit) sets it in state. The item continues to be returned after
|
|
14
|
+
* commit and is disposed when the hook unmounts.
|
|
15
|
+
*/
|
|
15
16
|
function useLazyDisposableState(parentCache) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return cleanupFn();
|
|
32
|
-
};
|
|
33
|
-
}, []);
|
|
34
|
-
const returnedItem = (_a = preCommitItem === null || preCommitItem === void 0 ? void 0 : preCommitItem.state) !== null && _a !== void 0 ? _a : (_b = itemCleanupPairRef.current) === null || _b === void 0 ? void 0 : _b[0];
|
|
35
|
-
if (returnedItem != null) {
|
|
36
|
-
return { state: returnedItem };
|
|
37
|
-
}
|
|
38
|
-
// Safety: This can't happen. For renders before the initial commit, preCommitItem
|
|
39
|
-
// is non-null. During the initial commit, we assign itemCleanupPairRef.current,
|
|
40
|
-
// so during subsequent renders, itemCleanupPairRef.current is non-null.
|
|
41
|
-
throw new Error('returnedItem was unexpectedly null. This indicates a bug in react-disposable-state.');
|
|
17
|
+
const itemCleanupPairRef = (0, react.useRef)(null);
|
|
18
|
+
const preCommitItem = require_useCachedResponsivePrecommitValue.useCachedResponsivePrecommitValue(parentCache, (pair) => {
|
|
19
|
+
itemCleanupPairRef.current?.[1]();
|
|
20
|
+
itemCleanupPairRef.current = pair;
|
|
21
|
+
});
|
|
22
|
+
(0, react.useEffect)(() => {
|
|
23
|
+
return () => {
|
|
24
|
+
const cleanupFn = itemCleanupPairRef.current?.[1];
|
|
25
|
+
if (cleanupFn == null) throw new Error("cleanupFn unexpectedly null. This indicates a bug in react-disposable-state.");
|
|
26
|
+
return cleanupFn();
|
|
27
|
+
};
|
|
28
|
+
}, []);
|
|
29
|
+
const returnedItem = preCommitItem?.state ?? itemCleanupPairRef.current?.[0];
|
|
30
|
+
if (returnedItem != null) return { state: returnedItem };
|
|
31
|
+
throw new Error("returnedItem was unexpectedly null. This indicates a bug in react-disposable-state.");
|
|
42
32
|
}
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
exports.useLazyDisposableState = useLazyDisposableState;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useCachedResponsivePrecommitValue } from "./useCachedResponsivePrecommitValue.mjs";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/useLazyDisposableState.ts
|
|
5
|
+
/**
|
|
6
|
+
* useLazyDisposableState<T>
|
|
7
|
+
* - Takes a mutable parent cache and a factory function
|
|
8
|
+
* - Returns { state: T }
|
|
9
|
+
*
|
|
10
|
+
* This lazily loads the disposable item using useCachedResponsivePrecommitValue, then
|
|
11
|
+
* (on commit) sets it in state. The item continues to be returned after
|
|
12
|
+
* commit and is disposed when the hook unmounts.
|
|
13
|
+
*/
|
|
14
|
+
function useLazyDisposableState(parentCache) {
|
|
15
|
+
const itemCleanupPairRef = useRef(null);
|
|
16
|
+
const preCommitItem = useCachedResponsivePrecommitValue(parentCache, (pair) => {
|
|
17
|
+
itemCleanupPairRef.current?.[1]();
|
|
18
|
+
itemCleanupPairRef.current = pair;
|
|
19
|
+
});
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
return () => {
|
|
22
|
+
const cleanupFn = itemCleanupPairRef.current?.[1];
|
|
23
|
+
if (cleanupFn == null) throw new Error("cleanupFn unexpectedly null. This indicates a bug in react-disposable-state.");
|
|
24
|
+
return cleanupFn();
|
|
25
|
+
};
|
|
26
|
+
}, []);
|
|
27
|
+
const returnedItem = preCommitItem?.state ?? itemCleanupPairRef.current?.[0];
|
|
28
|
+
if (returnedItem != null) return { state: returnedItem };
|
|
29
|
+
throw new Error("returnedItem was unexpectedly null. This indicates a bug in react-disposable-state.");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
export { useLazyDisposableState };
|
|
34
|
+
//# sourceMappingURL=useLazyDisposableState.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLazyDisposableState.mjs","names":[],"sources":["../src/useLazyDisposableState.ts"],"sourcesContent":["'use strict';\n\nimport type { ItemCleanupPair } from '@isograph/isograph-disposable-types';\nimport { useEffect, useRef } from 'react';\nimport type { ParentCache } from './ParentCache';\nimport { useCachedResponsivePrecommitValue } from './useCachedResponsivePrecommitValue';\nimport { type UnassignedState } from './useUpdatableDisposableState';\n\n/**\n * useLazyDisposableState<T>\n * - Takes a mutable parent cache and a factory function\n * - Returns { state: T }\n *\n * This lazily loads the disposable item using useCachedResponsivePrecommitValue, then\n * (on commit) sets it in state. The item continues to be returned after\n * commit and is disposed when the hook unmounts.\n */\nexport function useLazyDisposableState<T>(\n parentCache: ParentCache<Exclude<T, UnassignedState>>,\n): {\n state: T;\n} {\n const itemCleanupPairRef = useRef<ItemCleanupPair<T> | null>(null);\n\n const preCommitItem = useCachedResponsivePrecommitValue(\n parentCache,\n (pair) => {\n itemCleanupPairRef.current?.[1]();\n itemCleanupPairRef.current = pair;\n },\n );\n\n useEffect(() => {\n return () => {\n const cleanupFn = itemCleanupPairRef.current?.[1];\n // TODO confirm useEffect is called in order.\n if (cleanupFn == null) {\n throw new Error(\n 'cleanupFn unexpectedly null. This indicates a bug in react-disposable-state.',\n );\n }\n return cleanupFn();\n };\n }, []);\n\n const returnedItem = preCommitItem?.state ?? itemCleanupPairRef.current?.[0];\n\n if (returnedItem != null) {\n return { state: returnedItem };\n }\n\n // Safety: This can't happen. For renders before the initial commit, preCommitItem\n // is non-null. During the initial commit, we assign itemCleanupPairRef.current,\n // so during subsequent renders, itemCleanupPairRef.current is non-null.\n throw new Error(\n 'returnedItem was unexpectedly null. This indicates a bug in react-disposable-state.',\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAiBA,SAAgB,uBACd,aAGA;CACA,MAAM,qBAAqB,OAAkC,KAAK;CAElE,MAAM,gBAAgB,kCACpB,cACC,SAAS;AACR,qBAAmB,UAAU,IAAI;AACjC,qBAAmB,UAAU;GAEhC;AAED,iBAAgB;AACd,eAAa;GACX,MAAM,YAAY,mBAAmB,UAAU;AAE/C,OAAI,aAAa,KACf,OAAM,IAAI,MACR,+EACD;AAEH,UAAO,WAAW;;IAEnB,EAAE,CAAC;CAEN,MAAM,eAAe,eAAe,SAAS,mBAAmB,UAAU;AAE1E,KAAI,gBAAgB,KAClB,QAAO,EAAE,OAAO,cAAc;AAMhC,OAAM,IAAI,MACR,sFACD"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ItemCleanupPair } from "@isograph/disposable-types";
|
|
2
|
+
|
|
3
|
+
//#region src/useUpdatableDisposableState.d.ts
|
|
4
|
+
declare const UNASSIGNED_STATE: unique symbol;
|
|
5
|
+
type UnassignedState = typeof UNASSIGNED_STATE;
|
|
6
|
+
type UseUpdatableDisposableStateReturnValue<T> = {
|
|
7
|
+
state: T | UnassignedState;
|
|
8
|
+
setState: (pair: ItemCleanupPair<Exclude<T, UnassignedState>>) => void;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* useUpdatableDisposableState
|
|
12
|
+
* - Returns a { state, setItem } object.
|
|
13
|
+
* - setItem accepts an ItemCleanupPair<T>, and throws if called before commit.
|
|
14
|
+
* - setItem sets the T in state and adds it to a set.
|
|
15
|
+
* - React's behavior is that when the hook commits, whatever item is currently
|
|
16
|
+
* returned from the useState hook is the oldest item which will ever be returned
|
|
17
|
+
* from that useState hook. (More newly created ones can later be returned with
|
|
18
|
+
* concurrent mode.)
|
|
19
|
+
* - When this hook commits, all items up to, but not including, the item currently
|
|
20
|
+
* returned from the useState hook are disposed and removed from the set.
|
|
21
|
+
*
|
|
22
|
+
* Calling setState before the hook commits:
|
|
23
|
+
* - Calling setState before the hook commits is disallowed because until the hook
|
|
24
|
+
* commits, React will not schedule any unmount callbacks, meaning that if this
|
|
25
|
+
* hook never commits, any disposable items passed to setState will never be
|
|
26
|
+
* disposed.
|
|
27
|
+
* - We also cannot store them in some cache, because multiple components can share
|
|
28
|
+
* the same cache location (for example, if they are loading the same query from
|
|
29
|
+
* multiple components), so updating the cache with the disposable item will cause
|
|
30
|
+
* both components to show the updated data, which is almost certainly a bug.
|
|
31
|
+
* - Note that calling setState before commit is probably an anti-pattern! Consider
|
|
32
|
+
* not doing it.
|
|
33
|
+
* - If you must, the workaround is to lazily load the disposable item with
|
|
34
|
+
* useDisposableState or useLazyDisposableState, and update the cache location
|
|
35
|
+
* instead of calling setState before commit. One can update the cache location
|
|
36
|
+
* by calling setState on some parent component that has already mounted, and
|
|
37
|
+
* therefore passing in different props.
|
|
38
|
+
* - This may only work in concurrent mode, though.
|
|
39
|
+
*/
|
|
40
|
+
declare function useUpdatableDisposableState<T = never>(): UseUpdatableDisposableStateReturnValue<T>;
|
|
41
|
+
//#endregion
|
|
42
|
+
export { UNASSIGNED_STATE, UnassignedState, useUpdatableDisposableState };
|
|
43
|
+
//# sourceMappingURL=useUpdatableDisposableState.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useUpdatableDisposableState.d.mts","names":[],"sources":["../src/useUpdatableDisposableState.ts"],"sourcesContent":[],"mappings":";;;cAIa;KACD,eAAA,UAAyB;AADrC,KAGK,sCAHkD,CAAA,CAAA,CAAA,GAAA;EAC3C,KAAA,EAGH,CAHG,GAGC,eAHc;EAEtB,QAAA,EAAA,CAAA,IAAA,EAEc,eAFd,CAE8B,OAF9B,CAEsC,CAFA,EAEG,eAFH,CAAA,CAAA,EAAA,GAAA,IAAA;CAClC;;;;;;;AAoDT;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,0CAEX,uCAAuC"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { ItemCleanupPair } from
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { ItemCleanupPair } from "@isograph/disposable-types";
|
|
2
|
+
|
|
3
|
+
//#region src/useUpdatableDisposableState.d.ts
|
|
4
|
+
declare const UNASSIGNED_STATE: unique symbol;
|
|
5
|
+
type UnassignedState = typeof UNASSIGNED_STATE;
|
|
4
6
|
type UseUpdatableDisposableStateReturnValue<T> = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
state: T | UnassignedState;
|
|
8
|
+
setState: (pair: ItemCleanupPair<Exclude<T, UnassignedState>>) => void;
|
|
7
9
|
};
|
|
8
10
|
/**
|
|
9
11
|
* useUpdatableDisposableState
|
|
@@ -35,6 +37,7 @@ type UseUpdatableDisposableStateReturnValue<T> = {
|
|
|
35
37
|
* therefore passing in different props.
|
|
36
38
|
* - This may only work in concurrent mode, though.
|
|
37
39
|
*/
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
declare function useUpdatableDisposableState<T = never>(): UseUpdatableDisposableStateReturnValue<T>;
|
|
41
|
+
//#endregion
|
|
42
|
+
export { UNASSIGNED_STATE, UnassignedState, useUpdatableDisposableState };
|
|
40
43
|
//# sourceMappingURL=useUpdatableDisposableState.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useUpdatableDisposableState.d.ts","
|
|
1
|
+
{"version":3,"file":"useUpdatableDisposableState.d.ts","names":[],"sources":["../src/useUpdatableDisposableState.ts"],"sourcesContent":[],"mappings":";;;cAIa;KACD,eAAA,UAAyB;AADrC,KAGK,sCAHkD,CAAA,CAAA,CAAA,GAAA;EAC3C,KAAA,EAGH,CAHG,GAGC,eAHc;EAEtB,QAAA,EAAA,CAAA,IAAA,EAEc,eAFd,CAE8B,OAF9B,CAEsC,CAFA,EAEG,eAFH,CAAA,CAAA,EAAA,GAAA,IAAA;CAClC;;;;;;;AAoDT;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,0CAEX,uCAAuC"}
|
|
@@ -1,92 +1,76 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.js');
|
|
2
|
+
const require_useHasCommittedRef = require('./useHasCommittedRef.js');
|
|
3
|
+
let react = require("react");
|
|
4
|
+
react = require_rolldown_runtime.__toESM(react);
|
|
5
|
+
|
|
6
|
+
//#region src/useUpdatableDisposableState.ts
|
|
7
|
+
const UNASSIGNED_STATE = Symbol();
|
|
8
8
|
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
9
|
+
* useUpdatableDisposableState
|
|
10
|
+
* - Returns a { state, setItem } object.
|
|
11
|
+
* - setItem accepts an ItemCleanupPair<T>, and throws if called before commit.
|
|
12
|
+
* - setItem sets the T in state and adds it to a set.
|
|
13
|
+
* - React's behavior is that when the hook commits, whatever item is currently
|
|
14
|
+
* returned from the useState hook is the oldest item which will ever be returned
|
|
15
|
+
* from that useState hook. (More newly created ones can later be returned with
|
|
16
|
+
* concurrent mode.)
|
|
17
|
+
* - When this hook commits, all items up to, but not including, the item currently
|
|
18
|
+
* returned from the useState hook are disposed and removed from the set.
|
|
19
|
+
*
|
|
20
|
+
* Calling setState before the hook commits:
|
|
21
|
+
* - Calling setState before the hook commits is disallowed because until the hook
|
|
22
|
+
* commits, React will not schedule any unmount callbacks, meaning that if this
|
|
23
|
+
* hook never commits, any disposable items passed to setState will never be
|
|
24
|
+
* disposed.
|
|
25
|
+
* - We also cannot store them in some cache, because multiple components can share
|
|
26
|
+
* the same cache location (for example, if they are loading the same query from
|
|
27
|
+
* multiple components), so updating the cache with the disposable item will cause
|
|
28
|
+
* both components to show the updated data, which is almost certainly a bug.
|
|
29
|
+
* - Note that calling setState before commit is probably an anti-pattern! Consider
|
|
30
|
+
* not doing it.
|
|
31
|
+
* - If you must, the workaround is to lazily load the disposable item with
|
|
32
|
+
* useDisposableState or useLazyDisposableState, and update the cache location
|
|
33
|
+
* instead of calling setState before commit. One can update the cache location
|
|
34
|
+
* by calling setState on some parent component that has already mounted, and
|
|
35
|
+
* therefore passing in different props.
|
|
36
|
+
* - This may only work in concurrent mode, though.
|
|
37
|
+
*/
|
|
38
38
|
function useUpdatableDisposableState() {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
undisposedICI.cleanup();
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
}, []);
|
|
76
|
-
return {
|
|
77
|
-
setState: setStateAfterCommit,
|
|
78
|
-
state: stateICI !== exports.UNASSIGNED_STATE ? stateICI.item : exports.UNASSIGNED_STATE,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
// @ts-ignore
|
|
82
|
-
function tsTests() {
|
|
83
|
-
const a = useUpdatableDisposableState();
|
|
84
|
-
// @ts-expect-error
|
|
85
|
-
a.setState([exports.UNASSIGNED_STATE, () => { }]);
|
|
86
|
-
// @ts-expect-error
|
|
87
|
-
a.setState(['asdf', () => { }]);
|
|
88
|
-
const b = useUpdatableDisposableState();
|
|
89
|
-
// @ts-expect-error
|
|
90
|
-
b.setState([exports.UNASSIGNED_STATE, () => { }]);
|
|
91
|
-
b.setState(['asdf', () => { }]);
|
|
39
|
+
const hasCommittedRef = require_useHasCommittedRef.useHasCommittedRef();
|
|
40
|
+
const undisposedICIs = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
41
|
+
const setStateCountRef = (0, react.useRef)(0);
|
|
42
|
+
const [stateICI, setStateICI] = (0, react.useState)(UNASSIGNED_STATE);
|
|
43
|
+
const setStateAfterCommit = (0, react.useCallback)((itemCleanupPair) => {
|
|
44
|
+
if (!hasCommittedRef.current) throw new Error("Calling setState before the component has committed is unsafe and disallowed.");
|
|
45
|
+
const ici = {
|
|
46
|
+
item: itemCleanupPair[0],
|
|
47
|
+
cleanup: itemCleanupPair[1],
|
|
48
|
+
index: setStateCountRef.current
|
|
49
|
+
};
|
|
50
|
+
setStateCountRef.current++;
|
|
51
|
+
undisposedICIs.current.add(ici);
|
|
52
|
+
setStateICI(ici);
|
|
53
|
+
}, [setStateICI]);
|
|
54
|
+
(0, react.useEffect)(function cleanupUnreachableItems() {
|
|
55
|
+
const indexInState = stateICI !== UNASSIGNED_STATE ? stateICI.index : 0;
|
|
56
|
+
if (indexInState === 0) return;
|
|
57
|
+
for (const undisposedICI of undisposedICIs.current) {
|
|
58
|
+
if (undisposedICI.index === indexInState) break;
|
|
59
|
+
undisposedICIs.current.delete(undisposedICI);
|
|
60
|
+
undisposedICI.cleanup();
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
(0, react.useEffect)(() => {
|
|
64
|
+
return function disposeAllRemainingItems() {
|
|
65
|
+
for (const undisposedICI of undisposedICIs.current) undisposedICI.cleanup();
|
|
66
|
+
};
|
|
67
|
+
}, []);
|
|
68
|
+
return {
|
|
69
|
+
setState: setStateAfterCommit,
|
|
70
|
+
state: stateICI !== UNASSIGNED_STATE ? stateICI.item : UNASSIGNED_STATE
|
|
71
|
+
};
|
|
92
72
|
}
|
|
73
|
+
|
|
74
|
+
//#endregion
|
|
75
|
+
exports.UNASSIGNED_STATE = UNASSIGNED_STATE;
|
|
76
|
+
exports.useUpdatableDisposableState = useUpdatableDisposableState;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { useHasCommittedRef } from "./useHasCommittedRef.mjs";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/useUpdatableDisposableState.ts
|
|
5
|
+
const UNASSIGNED_STATE = Symbol();
|
|
6
|
+
/**
|
|
7
|
+
* useUpdatableDisposableState
|
|
8
|
+
* - Returns a { state, setItem } object.
|
|
9
|
+
* - setItem accepts an ItemCleanupPair<T>, and throws if called before commit.
|
|
10
|
+
* - setItem sets the T in state and adds it to a set.
|
|
11
|
+
* - React's behavior is that when the hook commits, whatever item is currently
|
|
12
|
+
* returned from the useState hook is the oldest item which will ever be returned
|
|
13
|
+
* from that useState hook. (More newly created ones can later be returned with
|
|
14
|
+
* concurrent mode.)
|
|
15
|
+
* - When this hook commits, all items up to, but not including, the item currently
|
|
16
|
+
* returned from the useState hook are disposed and removed from the set.
|
|
17
|
+
*
|
|
18
|
+
* Calling setState before the hook commits:
|
|
19
|
+
* - Calling setState before the hook commits is disallowed because until the hook
|
|
20
|
+
* commits, React will not schedule any unmount callbacks, meaning that if this
|
|
21
|
+
* hook never commits, any disposable items passed to setState will never be
|
|
22
|
+
* disposed.
|
|
23
|
+
* - We also cannot store them in some cache, because multiple components can share
|
|
24
|
+
* the same cache location (for example, if they are loading the same query from
|
|
25
|
+
* multiple components), so updating the cache with the disposable item will cause
|
|
26
|
+
* both components to show the updated data, which is almost certainly a bug.
|
|
27
|
+
* - Note that calling setState before commit is probably an anti-pattern! Consider
|
|
28
|
+
* not doing it.
|
|
29
|
+
* - If you must, the workaround is to lazily load the disposable item with
|
|
30
|
+
* useDisposableState or useLazyDisposableState, and update the cache location
|
|
31
|
+
* instead of calling setState before commit. One can update the cache location
|
|
32
|
+
* by calling setState on some parent component that has already mounted, and
|
|
33
|
+
* therefore passing in different props.
|
|
34
|
+
* - This may only work in concurrent mode, though.
|
|
35
|
+
*/
|
|
36
|
+
function useUpdatableDisposableState() {
|
|
37
|
+
const hasCommittedRef = useHasCommittedRef();
|
|
38
|
+
const undisposedICIs = useRef(/* @__PURE__ */ new Set());
|
|
39
|
+
const setStateCountRef = useRef(0);
|
|
40
|
+
const [stateICI, setStateICI] = useState(UNASSIGNED_STATE);
|
|
41
|
+
const setStateAfterCommit = useCallback((itemCleanupPair) => {
|
|
42
|
+
if (!hasCommittedRef.current) throw new Error("Calling setState before the component has committed is unsafe and disallowed.");
|
|
43
|
+
const ici = {
|
|
44
|
+
item: itemCleanupPair[0],
|
|
45
|
+
cleanup: itemCleanupPair[1],
|
|
46
|
+
index: setStateCountRef.current
|
|
47
|
+
};
|
|
48
|
+
setStateCountRef.current++;
|
|
49
|
+
undisposedICIs.current.add(ici);
|
|
50
|
+
setStateICI(ici);
|
|
51
|
+
}, [setStateICI]);
|
|
52
|
+
useEffect(function cleanupUnreachableItems() {
|
|
53
|
+
const indexInState = stateICI !== UNASSIGNED_STATE ? stateICI.index : 0;
|
|
54
|
+
if (indexInState === 0) return;
|
|
55
|
+
for (const undisposedICI of undisposedICIs.current) {
|
|
56
|
+
if (undisposedICI.index === indexInState) break;
|
|
57
|
+
undisposedICIs.current.delete(undisposedICI);
|
|
58
|
+
undisposedICI.cleanup();
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
return function disposeAllRemainingItems() {
|
|
63
|
+
for (const undisposedICI of undisposedICIs.current) undisposedICI.cleanup();
|
|
64
|
+
};
|
|
65
|
+
}, []);
|
|
66
|
+
return {
|
|
67
|
+
setState: setStateAfterCommit,
|
|
68
|
+
state: stateICI !== UNASSIGNED_STATE ? stateICI.item : UNASSIGNED_STATE
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
//#endregion
|
|
73
|
+
export { UNASSIGNED_STATE, useUpdatableDisposableState };
|
|
74
|
+
//# sourceMappingURL=useUpdatableDisposableState.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useUpdatableDisposableState.mjs","names":["UNASSIGNED_STATE: unique symbol","ici: ICI<T>"],"sources":["../src/useUpdatableDisposableState.ts"],"sourcesContent":["import type { ItemCleanupPair } from '@isograph/disposable-types';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useHasCommittedRef } from './useHasCommittedRef';\n\nexport const UNASSIGNED_STATE: unique symbol = Symbol();\nexport type UnassignedState = typeof UNASSIGNED_STATE;\n\ntype UseUpdatableDisposableStateReturnValue<T> = {\n state: T | UnassignedState;\n setState: (pair: ItemCleanupPair<Exclude<T, UnassignedState>>) => void;\n};\n\n/**\n * ICI stands for ItemCleanupIndex. Use a short name because it shows up a lot.\n *\n * Like an ItemCleanupPair<T>, but with an additional index and as a struct\n * for readability.\n *\n * Interally, we must keep track of the index of the state change. The disposable\n * items that are set in state are not guaranteed to be distinct according to ===,\n * as in `setState([1, dispose1]); setState([1, dispose2])`. Following this, we\n * would expect dispose1 to be called on commit. If state items were compared for\n * ===, then dispose1 would not be called in such a situation.\n *\n * Note that this comes at a runtime cost, and we may want to expose APIs that do\n * not incur this runtime cost, for example in cases where every disposable item is\n * distinguishable via ===.\n */\ntype ICI<T> = { item: T; cleanup: () => void; index: number };\n\n/**\n * useUpdatableDisposableState\n * - Returns a { state, setItem } object.\n * - setItem accepts an ItemCleanupPair<T>, and throws if called before commit.\n * - setItem sets the T in state and adds it to a set.\n * - React's behavior is that when the hook commits, whatever item is currently\n * returned from the useState hook is the oldest item which will ever be returned\n * from that useState hook. (More newly created ones can later be returned with\n * concurrent mode.)\n * - When this hook commits, all items up to, but not including, the item currently\n * returned from the useState hook are disposed and removed from the set.\n *\n * Calling setState before the hook commits:\n * - Calling setState before the hook commits is disallowed because until the hook\n * commits, React will not schedule any unmount callbacks, meaning that if this\n * hook never commits, any disposable items passed to setState will never be\n * disposed.\n * - We also cannot store them in some cache, because multiple components can share\n * the same cache location (for example, if they are loading the same query from\n * multiple components), so updating the cache with the disposable item will cause\n * both components to show the updated data, which is almost certainly a bug.\n * - Note that calling setState before commit is probably an anti-pattern! Consider\n * not doing it.\n * - If you must, the workaround is to lazily load the disposable item with\n * useDisposableState or useLazyDisposableState, and update the cache location\n * instead of calling setState before commit. One can update the cache location\n * by calling setState on some parent component that has already mounted, and\n * therefore passing in different props.\n * - This may only work in concurrent mode, though.\n */\nexport function useUpdatableDisposableState<\n T = never,\n>(): UseUpdatableDisposableStateReturnValue<T> {\n const hasCommittedRef = useHasCommittedRef();\n\n const undisposedICIs = useRef(new Set<ICI<T>>());\n const setStateCountRef = useRef(0);\n\n const [stateICI, setStateICI] = useState<ICI<T> | UnassignedState>(\n UNASSIGNED_STATE,\n );\n\n const setStateAfterCommit = useCallback(\n (itemCleanupPair: ItemCleanupPair<T>) => {\n if (!hasCommittedRef.current) {\n throw new Error(\n 'Calling setState before the component has committed is unsafe and disallowed.',\n );\n }\n\n const ici: ICI<T> = {\n item: itemCleanupPair[0],\n cleanup: itemCleanupPair[1],\n index: setStateCountRef.current,\n };\n setStateCountRef.current++;\n undisposedICIs.current.add(ici);\n setStateICI(ici);\n },\n [setStateICI],\n );\n\n useEffect(function cleanupUnreachableItems() {\n const indexInState = stateICI !== UNASSIGNED_STATE ? stateICI.index : 0;\n\n if (indexInState === 0) {\n return;\n }\n\n for (const undisposedICI of undisposedICIs.current) {\n if (undisposedICI.index === indexInState) {\n break;\n }\n undisposedICIs.current.delete(undisposedICI);\n undisposedICI.cleanup();\n }\n });\n\n useEffect(() => {\n return function disposeAllRemainingItems() {\n for (const undisposedICI of undisposedICIs.current) {\n undisposedICI.cleanup();\n }\n };\n }, []);\n\n return {\n setState: setStateAfterCommit,\n state: stateICI !== UNASSIGNED_STATE ? stateICI.item : UNASSIGNED_STATE,\n };\n}\n\n// @ts-ignore\nfunction tsTests() {\n const a = useUpdatableDisposableState();\n // @ts-expect-error\n a.setState([UNASSIGNED_STATE, () => {}]);\n // @ts-expect-error\n a.setState(['asdf', () => {}]);\n const b = useUpdatableDisposableState<string | UnassignedState>();\n // @ts-expect-error\n b.setState([UNASSIGNED_STATE, () => {}]);\n b.setState(['asdf', () => {}]);\n}\n"],"mappings":";;;;AAIA,MAAaA,mBAAkC,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDvD,SAAgB,8BAE+B;CAC7C,MAAM,kBAAkB,oBAAoB;CAE5C,MAAM,iBAAiB,uBAAO,IAAI,KAAa,CAAC;CAChD,MAAM,mBAAmB,OAAO,EAAE;CAElC,MAAM,CAAC,UAAU,eAAe,SAC9B,iBACD;CAED,MAAM,sBAAsB,aACzB,oBAAwC;AACvC,MAAI,CAAC,gBAAgB,QACnB,OAAM,IAAI,MACR,gFACD;EAGH,MAAMC,MAAc;GAClB,MAAM,gBAAgB;GACtB,SAAS,gBAAgB;GACzB,OAAO,iBAAiB;GACzB;AACD,mBAAiB;AACjB,iBAAe,QAAQ,IAAI,IAAI;AAC/B,cAAY,IAAI;IAElB,CAAC,YAAY,CACd;AAED,WAAU,SAAS,0BAA0B;EAC3C,MAAM,eAAe,aAAa,mBAAmB,SAAS,QAAQ;AAEtE,MAAI,iBAAiB,EACnB;AAGF,OAAK,MAAM,iBAAiB,eAAe,SAAS;AAClD,OAAI,cAAc,UAAU,aAC1B;AAEF,kBAAe,QAAQ,OAAO,cAAc;AAC5C,iBAAc,SAAS;;GAEzB;AAEF,iBAAgB;AACd,SAAO,SAAS,2BAA2B;AACzC,QAAK,MAAM,iBAAiB,eAAe,QACzC,eAAc,SAAS;;IAG1B,EAAE,CAAC;AAEN,QAAO;EACL,UAAU;EACV,OAAO,aAAa,mBAAmB,SAAS,OAAO;EACxD"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@isograph/react-disposable-state",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Primitives for managing disposable state in React",
|
|
5
5
|
"homepage": "https://isograph.dev",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
8
|
"author": "Isograph Labs",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@isograph/disposable-types": "0.5.
|
|
11
|
+
"@isograph/disposable-types": "0.5.1"
|
|
12
12
|
},
|
|
13
13
|
"peerDependencies": {
|
|
14
14
|
"react": "^18.0.0 || ^19.0.0"
|
|
@@ -24,9 +24,17 @@
|
|
|
24
24
|
"directory": "libs/isograph-react-disposable-state"
|
|
25
25
|
},
|
|
26
26
|
"sideEffects": false,
|
|
27
|
+
"module": "./dist/index.mjs",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"import": "./dist/index.mjs",
|
|
31
|
+
"require": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./package.json": "./package.json"
|
|
34
|
+
},
|
|
27
35
|
"scripts": {
|
|
28
|
-
"compile-libs": "
|
|
29
|
-
"
|
|
36
|
+
"compile-libs": "tsdown",
|
|
37
|
+
"watch-libs": "tsdown --watch",
|
|
30
38
|
"test": "vitest run",
|
|
31
39
|
"test-watch": "vitest watch",
|
|
32
40
|
"coverage": "vitest run --coverage",
|