@isograph/react-disposable-state 0.5.2 → 0.5.5

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.
Files changed (96) hide show
  1. package/.turbo/turbo-compile-libs.log +5 -5
  2. package/.turbo/turbo-tsc.log +5 -0
  3. package/README.md +19 -0
  4. package/dist/{CacheItem.js → CacheItem.cjs} +11 -8
  5. package/dist/{CacheItem.d.ts → CacheItem.d.cts} +1 -1
  6. package/dist/CacheItem.d.cts.map +1 -0
  7. package/dist/CacheItem.d.mts.map +1 -1
  8. package/dist/CacheItem.mjs +11 -8
  9. package/dist/CacheItem.mjs.map +1 -1
  10. package/dist/{ParentCache.js → ParentCache.cjs} +4 -3
  11. package/dist/{ParentCache.d.ts → ParentCache.d.cts} +4 -4
  12. package/dist/ParentCache.d.cts.map +1 -0
  13. package/dist/ParentCache.d.mts +3 -3
  14. package/dist/ParentCache.d.mts.map +1 -1
  15. package/dist/ParentCache.mjs +3 -2
  16. package/dist/ParentCache.mjs.map +1 -1
  17. package/dist/{index.js → index.cjs} +14 -10
  18. package/dist/{index.d.ts → index.d.cts} +10 -8
  19. package/dist/index.d.mts +3 -1
  20. package/dist/index.mjs +3 -1
  21. package/dist/{useCachedResponsivePrecommitValue.js → useCachedResponsivePrecommitValue.cjs} +15 -9
  22. package/dist/{useCachedResponsivePrecommitValue.d.ts → useCachedResponsivePrecommitValue.d.cts} +2 -3
  23. package/dist/useCachedResponsivePrecommitValue.d.cts.map +1 -0
  24. package/dist/useCachedResponsivePrecommitValue.d.mts +0 -1
  25. package/dist/useCachedResponsivePrecommitValue.d.mts.map +1 -1
  26. package/dist/useCachedResponsivePrecommitValue.mjs +13 -7
  27. package/dist/useCachedResponsivePrecommitValue.mjs.map +1 -1
  28. package/dist/{useDisposableState.js → useDisposableState.cjs} +9 -10
  29. package/dist/{useDisposableState.d.ts → useDisposableState.d.cts} +3 -3
  30. package/dist/useDisposableState.d.cts.map +1 -0
  31. package/dist/useDisposableState.d.mts.map +1 -1
  32. package/dist/useDisposableState.mjs +7 -6
  33. package/dist/useDisposableState.mjs.map +1 -1
  34. package/dist/useEffectsRerunWithoutRender.cjs +27 -0
  35. package/dist/useEffectsRerunWithoutRender.d.cts +19 -0
  36. package/dist/useEffectsRerunWithoutRender.d.cts.map +1 -0
  37. package/dist/useEffectsRerunWithoutRender.d.mts +19 -0
  38. package/dist/useEffectsRerunWithoutRender.d.mts.map +1 -0
  39. package/dist/useEffectsRerunWithoutRender.mjs +28 -0
  40. package/dist/useEffectsRerunWithoutRender.mjs.map +1 -0
  41. package/dist/useHasCommittedRef.cjs +20 -0
  42. package/dist/useHasCommittedRef.d.cts +14 -0
  43. package/dist/useHasCommittedRef.d.cts.map +1 -0
  44. package/dist/useHasCommittedRef.d.mts +4 -1
  45. package/dist/useHasCommittedRef.d.mts.map +1 -1
  46. package/dist/useHasCommittedRef.mjs +6 -2
  47. package/dist/useHasCommittedRef.mjs.map +1 -1
  48. package/dist/{useLazyDisposableState.js → useLazyDisposableState.cjs} +6 -6
  49. package/dist/{useLazyDisposableState.d.ts → useLazyDisposableState.d.cts} +3 -4
  50. package/dist/useLazyDisposableState.d.cts.map +1 -0
  51. package/dist/useLazyDisposableState.d.mts +0 -1
  52. package/dist/useLazyDisposableState.d.mts.map +1 -1
  53. package/dist/useLazyDisposableState.mjs +3 -3
  54. package/dist/useLazyDisposableState.mjs.map +1 -1
  55. package/dist/useUpdatableDisposableClearableState.cjs +36 -0
  56. package/dist/useUpdatableDisposableClearableState.d.cts +26 -0
  57. package/dist/useUpdatableDisposableClearableState.d.cts.map +1 -0
  58. package/dist/useUpdatableDisposableClearableState.d.mts +26 -0
  59. package/dist/useUpdatableDisposableClearableState.d.mts.map +1 -0
  60. package/dist/useUpdatableDisposableClearableState.mjs +37 -0
  61. package/dist/useUpdatableDisposableClearableState.mjs.map +1 -0
  62. package/dist/{useUpdatableDisposableState.js → useUpdatableDisposableState.cjs} +6 -4
  63. package/dist/{useUpdatableDisposableState.d.ts → useUpdatableDisposableState.d.cts} +1 -1
  64. package/dist/useUpdatableDisposableState.d.cts.map +1 -0
  65. package/dist/useUpdatableDisposableState.d.mts.map +1 -1
  66. package/dist/useUpdatableDisposableState.mjs +5 -1
  67. package/dist/useUpdatableDisposableState.mjs.map +1 -1
  68. package/package.json +9 -6
  69. package/src/CacheItem.test.ts +40 -2
  70. package/src/CacheItem.ts +1 -0
  71. package/src/ParentCache.ts +26 -21
  72. package/src/index.ts +2 -0
  73. package/src/useCachedResponsivePrecommitValue.test.tsx +8 -3
  74. package/src/useCachedResponsivePrecommitValue.ts +32 -20
  75. package/src/useDisposableState.ts +13 -15
  76. package/src/useEffectsRerunWithoutRender.test.tsx +54 -0
  77. package/src/useEffectsRerunWithoutRender.ts +23 -0
  78. package/src/useHasCommittedRef.ts +6 -2
  79. package/src/useLazyDisposableState.test.tsx +73 -34
  80. package/src/useLazyDisposableState.ts +4 -5
  81. package/src/useUpdatableDisposableClearableState.ts +67 -0
  82. package/src/useUpdatableDisposableState.test.tsx +55 -4
  83. package/src/useUpdatableDisposableState.ts +2 -0
  84. package/tsconfig.json +2 -4
  85. package/vitest.config.ts +8 -0
  86. package/dist/CacheItem.d.ts.map +0 -1
  87. package/dist/ParentCache.d.ts.map +0 -1
  88. package/dist/_virtual/rolldown_runtime.js +0 -25
  89. package/dist/useCachedResponsivePrecommitValue.d.ts.map +0 -1
  90. package/dist/useDisposableState.d.ts.map +0 -1
  91. package/dist/useHasCommittedRef.d.ts +0 -11
  92. package/dist/useHasCommittedRef.d.ts.map +0 -1
  93. package/dist/useHasCommittedRef.js +0 -18
  94. package/dist/useLazyDisposableState.d.ts.map +0 -1
  95. package/dist/useUpdatableDisposableState.d.ts.map +0 -1
  96. package/tsconfig.pkg.json +0 -9
@@ -0,0 +1,67 @@
1
+ import type { ItemCleanupPair } from '@isograph/disposable-types';
2
+ import { useCallback } from 'react';
3
+ import {
4
+ UNASSIGNED_STATE,
5
+ type UnassignedState,
6
+ useUpdatableDisposableState,
7
+ } from './useUpdatableDisposableState';
8
+
9
+ type UseUpdatableDisposableClearableStateReturnValue<T> = {
10
+ state: T | UnassignedState;
11
+ setState: (pair: ItemCleanupPair<Exclude<T, UnassignedState>>) => void;
12
+ clearState: () => void;
13
+ };
14
+
15
+ /**
16
+ * The item that `clearState` puts in the underlying state. It is never returned
17
+ * from this hook; the hook reports UNASSIGNED_STATE while it is current.
18
+ */
19
+ const CLEARED_STATE: unique symbol = Symbol();
20
+ type ClearedState = typeof CLEARED_STATE;
21
+
22
+ function noop() {}
23
+
24
+ /**
25
+ * useUpdatableDisposableClearableState
26
+ * - useUpdatableDisposableState, plus clearState.
27
+ * - clearState returns the state to UNASSIGNED_STATE. The item that was in state
28
+ * is disposed on the next commit, exactly as if it had been superseded by
29
+ * setState.
30
+ * - Like setState, clearState throws if called before the initial commit.
31
+ *
32
+ * This is a wrapper around useUpdatableDisposableState: clearing sets a sentinel
33
+ * item with no cleanup, so the underlying hook's commit-time disposal of
34
+ * superseded items disposes the cleared item, and the sentinel is mapped back to
35
+ * UNASSIGNED_STATE on the way out.
36
+ */
37
+ export function useUpdatableDisposableClearableState<
38
+ T = never,
39
+ >(): UseUpdatableDisposableClearableStateReturnValue<T> {
40
+ const { state, setState } = useUpdatableDisposableState<T | ClearedState>();
41
+
42
+ const clearState = useCallback(() => {
43
+ setState([CLEARED_STATE, noop]);
44
+ }, [setState]);
45
+
46
+ return {
47
+ clearState,
48
+ setState,
49
+ state: state === CLEARED_STATE ? UNASSIGNED_STATE : state,
50
+ };
51
+ }
52
+
53
+ // @ts-ignore
54
+ function tsTests() {
55
+ const a = useUpdatableDisposableClearableState();
56
+ // @ts-expect-error
57
+ a.setState([UNASSIGNED_STATE, () => {}]);
58
+ // @ts-expect-error
59
+ a.setState(['asdf', () => {}]);
60
+ const b = useUpdatableDisposableClearableState<string | UnassignedState>();
61
+ // @ts-expect-error
62
+ b.setState([UNASSIGNED_STATE, () => {}]);
63
+ b.setState(['asdf', () => {}]);
64
+ // The cleared sentinel is not part of T, so it cannot be set from outside.
65
+ // @ts-expect-error
66
+ b.setState([CLEARED_STATE, () => {}]);
67
+ }
@@ -1,4 +1,11 @@
1
- import React, { type MutableRefObject } from 'react';
1
+ import type { ItemCleanupPair } from '@isograph/disposable-types';
2
+ import { render, screen } from '@testing-library/react';
3
+ import React, {
4
+ type MutableRefObject,
5
+ StrictMode,
6
+ useEffect,
7
+ useLayoutEffect,
8
+ } from 'react';
2
9
  import { create } from 'react-test-renderer';
3
10
  import { describe, expect, test, vi } from 'vitest';
4
11
  import {
@@ -51,15 +58,59 @@ function promiseAndResolver() {
51
58
  // not is a bit worrisome.
52
59
  async function awaitableCreate(Component, isConcurrent: boolean) {
53
60
  const element = create(
54
- Component,
61
+ <StrictMode>{Component}</StrictMode>,
62
+
55
63
  isConcurrent ? { unstable_isConcurrent: true } : undefined,
56
64
  );
57
65
  await shortPromise();
58
66
  return element;
59
67
  }
60
68
 
61
- describe('nothing', () => {
62
- test('it should pass', () => {});
69
+ describe('useUpdatableDisposableState during its first commit', () => {
70
+ // A child's effects run before its parent's effects of the same kind, and every layout effect runs before any
71
+ // passive effect, so a child effect runs after the parent's DOM is committed and before the parent's own effects.
72
+ // DOM event handlers of the parent can also run in that window.
73
+ test.each([
74
+ ['useLayoutEffect', useLayoutEffect],
75
+ ['useEffect', useEffect],
76
+ ])(
77
+ 'setState called from a child %s puts the item in state, and the item is disposed on unmount',
78
+ (_effectName, useChildEffect) => {
79
+ const dispose = vi.fn();
80
+ let setStateError: unknown = null;
81
+ function Child({
82
+ setState,
83
+ }: {
84
+ setState: (pair: ItemCleanupPair<number>) => void;
85
+ }) {
86
+ useChildEffect(() => {
87
+ try {
88
+ setState([1, dispose]);
89
+ } catch (error) {
90
+ setStateError = error;
91
+ }
92
+ }, []);
93
+ return null;
94
+ }
95
+ function Owner() {
96
+ const { state, setState } = useUpdatableDisposableState<number>();
97
+ return (
98
+ <>
99
+ {state === UNASSIGNED_STATE ? 'unassigned' : `item ${state}`}
100
+ <Child setState={setState} />
101
+ </>
102
+ );
103
+ }
104
+
105
+ const { unmount } = render(<Owner />);
106
+
107
+ expect(setStateError).toBeNull();
108
+ expect(screen.getByText('item 1')).toBeTruthy();
109
+ expect(dispose).not.toHaveBeenCalled();
110
+ unmount();
111
+ expect(dispose).toHaveBeenCalledTimes(1);
112
+ },
113
+ );
63
114
  });
64
115
 
65
116
  // Temporarily disable unit tests until flakiness is investigated
@@ -109,8 +109,10 @@ export function useUpdatableDisposableState<
109
109
  useEffect(() => {
110
110
  return function disposeAllRemainingItems() {
111
111
  for (const undisposedICI of undisposedICIs.current) {
112
+ undisposedICIs.current.delete(undisposedICI);
112
113
  undisposedICI.cleanup();
113
114
  }
115
+ setStateICI(UNASSIGNED_STATE);
114
116
  };
115
117
  }, []);
116
118
 
package/tsconfig.json CHANGED
@@ -1,6 +1,4 @@
1
1
  {
2
- "extends": "./tsconfig.pkg.json",
3
- "compilerOptions": {
4
- "noEmit": true
5
- }
2
+ "extends": "../../tsconfig.build.json",
3
+ "include": ["./**/*.ts"]
6
4
  }
@@ -0,0 +1,8 @@
1
+ import { defineProject } from 'vitest/config';
2
+
3
+ export default defineProject({
4
+ test: {
5
+ globals: true,
6
+ environment: 'happy-dom',
7
+ },
8
+ });
@@ -1 +0,0 @@
1
- {"version":3,"file":"CacheItem.d.ts","names":[],"sources":["../src/CacheItem.ts"],"sourcesContent":[],"mappings":";;;KAQY,2BAAA;;AAAZ,CAAA;AAGY,KAAA,8BAA8B,CAAA,CAAA,CAAA,GAAA;EAQ9B,IAAA,EAAA,gCAA2B;EAa3B,KAAA,EAnBH,CAmBG;EACoB,YAAA,EAAA,GAAA,GAAA,IAAA;EAA5B,oBAAA,EAAA,MAAA;CAC+B;AAA/B,KAfQ,2BAeR,CAAA,CAAA,CAAA,GAAA;EACA,IAAA,EAAA,6BAAA;EAA2B,KAAA,EAdtB,CAcsB;EAEnB,YAAA,EAAA,GAAA,GAAgB,IAAA;EA+Bf,qBAAS,EAAA,GAAA,GAAA,IAAA;EAUD,oBAAA,EAAA,MAAA;EAAR,oBAAA,EAAA,MAAA;CACc;AACd,KAhDD,cAgDC,CAAA,CAAA,CAAA,GA/CT,2BA+CS,CA/CmB,CA+CnB,CAAA,GA9CT,8BA8CS,CA9CsB,CA8CtB,CAAA,GA7CT,2BA6CS;AAgBC,KA3DF,gBAAA,GA2DE;EAkBgB,mBAAA,EAAA,MAAA;CACT;;;;;AAyPrB;;;;;;;;;;;;;;;;;;;;cAxSa;;;uBAUA,QAAQ,2BACM,oBACd;cAgBC;yDAkBgB,YACzB,gBAAgB;qBA+EA;qBAqEA;;;;iBAqGL,+CACL,QAAQ,2BACM,oBACd,2BACP,UAAU,IAAI"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ParentCache.d.ts","names":[],"sources":["../src/ParentCache.ts"],"sourcesContent":[],"mappings":";;;;;;;AA2BA;;;;;;;;;;;;;;cAAa;;;uBAMU,QAAQ;;;;;;;;;;;oCAcK,gBAAgB;sCAMd,UAAU,IAAI,GAAG;;;iBAsCtC,QAAQ"}
@@ -1,25 +0,0 @@
1
- //#region rolldown:runtime
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
- key = keys[i];
11
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
- get: ((k) => from[k]).bind(null, key),
13
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
- });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
- value: mod,
20
- enumerable: true
21
- }) : target, mod));
22
-
23
- //#endregion
24
-
25
- exports.__toESM = __toESM;
@@ -1 +0,0 @@
1
- {"version":3,"file":"useCachedResponsivePrecommitValue.d.ts","names":[],"sources":["../src/useCachedResponsivePrecommitValue.ts"],"sourcesContent":[],"mappings":";;;;;;;AAsCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,kDACD,YAAY,qBACR,gBAAgB;SACvB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"useDisposableState.d.ts","names":[],"sources":["../src/useDisposableState.ts"],"sourcesContent":[],"mappings":";;;;;KAUK;SACI;EADJ,QAAA,EAAA,CAAA,IAAA,EAEc,eAFd,CAE8B,OAF9B,CAEsC,CAFA,EAEG,eAFH,CAAA,CAAA,EAAA,GAAA,IAAA;CAClC;AACkC,iBAG3B,kBAH2B,CAAA,IAAA,KAAA,CAAA,CAAA,WAAA,EAI5B,WAJ4B,CAIhB,CAJgB,CAAA,CAAA,EAKxC,sCALwC,CAKD,CALC,CAAA"}
@@ -1,11 +0,0 @@
1
- import { MutableRefObject } from "react";
2
-
3
- //#region src/useHasCommittedRef.d.ts
4
-
5
- /**
6
- * Returns true if the component has committed, false otherwise.
7
- */
8
- declare function useHasCommittedRef(): MutableRefObject<boolean>;
9
- //#endregion
10
- export { useHasCommittedRef };
11
- //# sourceMappingURL=useHasCommittedRef.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useHasCommittedRef.d.ts","names":[],"sources":["../src/useHasCommittedRef.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;iBAAgB,kBAAA,CAAA,GAAsB"}
@@ -1,18 +0,0 @@
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
6
- /**
7
- * Returns true if the component has committed, false otherwise.
8
- */
9
- function useHasCommittedRef() {
10
- const hasCommittedRef = (0, react.useRef)(false);
11
- (0, react.useEffect)(() => {
12
- hasCommittedRef.current = true;
13
- }, []);
14
- return hasCommittedRef;
15
- }
16
-
17
- //#endregion
18
- exports.useHasCommittedRef = useHasCommittedRef;
@@ -1 +0,0 @@
1
- {"version":3,"file":"useLazyDisposableState.d.ts","names":[],"sources":["../src/useLazyDisposableState.ts"],"sourcesContent":[],"mappings":";;;;;;;AAiBA;;;;;;;iBAAgB,uCACD,YAAY,QAAQ,GAAG;SAE7B"}
@@ -1 +0,0 @@
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"}
package/tsconfig.pkg.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.build.json",
3
- "compilerOptions": {
4
- "outDir": "./dist/",
5
- "rootDir": "./src/",
6
- "declaration": true
7
- },
8
- "include": ["./**/*.ts"]
9
- }