@isograph/react-disposable-state 0.0.0-main-27034e7e → 0.0.0-main-977768e2

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.
@@ -1,11 +1,11 @@
1
1
  ../.. |  WARN  Unsupported engine: wanted: {"node":"24.12.0"} (current: {"node":"v24.21.0","pnpm":"10.15.0"})
2
2
 
3
- > @isograph/react-disposable-state@0.0.0-main-27034e7e compile-libs /home/runner/work/isograph/isograph/libs/isograph-react-disposable-state
3
+ > @isograph/react-disposable-state@0.0.0-main-977768e2 compile-libs /home/runner/work/isograph/isograph/libs/isograph-react-disposable-state
4
4
  > tsdown
5
5
 
6
6
  ℹ tsdown v0.20.1 powered by rolldown v1.0.0-rc.1
7
7
  ℹ config file: /home/runner/work/isograph/isograph/tsdown.config.ts
8
- (node:2574) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///home/runner/work/isograph/isograph/tsdown.config.ts?no-cache=6926acb1-ee03-4547-be0b-7c34774d56c7 is not specified and it doesn't parse as CommonJS.
8
+ (node:2716) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///home/runner/work/isograph/isograph/tsdown.config.ts?no-cache=c38b79da-4d1e-4698-99ba-2539b6d3ec4a is not specified and it doesn't parse as CommonJS.
9
9
  Reparsing as ES module because module syntax was detected. This incurs a performance overhead.
10
10
  To eliminate this warning, add "type": "module" to /home/runner/work/isograph/isograph/package.json.
11
11
  (Use `node --trace-warnings ...` to show where the warning was created)
@@ -1,5 +1,5 @@
1
1
  ../.. |  WARN  Unsupported engine: wanted: {"node":"24.12.0"} (current: {"node":"v24.21.0","pnpm":"10.15.0"})
2
2
 
3
- > @isograph/react-disposable-state@0.0.0-main-27034e7e tsc /home/runner/work/isograph/isograph/libs/isograph-react-disposable-state
3
+ > @isograph/react-disposable-state@0.0.0-main-977768e2 tsc /home/runner/work/isograph/isograph/libs/isograph-react-disposable-state
4
4
  > tsc
5
5
 
@@ -3,10 +3,14 @@ let react = require("react");
3
3
  //#region src/useHasCommittedRef.ts
4
4
  /**
5
5
  * Returns true if the component has committed, false otherwise.
6
+ *
7
+ * Set in an insertion effect, which React runs in the mutation phase of the commit, before any layout or passive
8
+ * effect of the commit. Layout and passive effects of the component's children, and DOM event handlers that run
9
+ * before the passive effects are flushed, therefore read true.
6
10
  */
7
11
  function useHasCommittedRef() {
8
12
  const hasCommittedRef = (0, react.useRef)(false);
9
- (0, react.useEffect)(() => {
13
+ (0, react.useInsertionEffect)(() => {
10
14
  hasCommittedRef.current = true;
11
15
  }, []);
12
16
  return hasCommittedRef;
@@ -3,6 +3,10 @@ import { MutableRefObject } from "react";
3
3
  //#region src/useHasCommittedRef.d.ts
4
4
  /**
5
5
  * Returns true if the component has committed, false otherwise.
6
+ *
7
+ * Set in an insertion effect, which React runs in the mutation phase of the commit, before any layout or passive
8
+ * effect of the commit. Layout and passive effects of the component's children, and DOM event handlers that run
9
+ * before the passive effects are flushed, therefore read true.
6
10
  */
7
11
  declare function useHasCommittedRef(): MutableRefObject<boolean>;
8
12
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"useHasCommittedRef.d.cts","names":[],"sources":["../src/useHasCommittedRef.ts"],"mappings":";;;;;AAKA;iBAAgB,kBAAA,CAAA,GAAsB,gBAAA"}
1
+ {"version":3,"file":"useHasCommittedRef.d.cts","names":[],"sources":["../src/useHasCommittedRef.ts"],"mappings":";;;;;AASA;;;;;iBAAgB,kBAAA,CAAA,GAAsB,gBAAA"}
@@ -3,6 +3,10 @@ import { MutableRefObject } from "react";
3
3
  //#region src/useHasCommittedRef.d.ts
4
4
  /**
5
5
  * Returns true if the component has committed, false otherwise.
6
+ *
7
+ * Set in an insertion effect, which React runs in the mutation phase of the commit, before any layout or passive
8
+ * effect of the commit. Layout and passive effects of the component's children, and DOM event handlers that run
9
+ * before the passive effects are flushed, therefore read true.
6
10
  */
7
11
  declare function useHasCommittedRef(): MutableRefObject<boolean>;
8
12
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"useHasCommittedRef.d.mts","names":[],"sources":["../src/useHasCommittedRef.ts"],"mappings":";;;;;AAKA;iBAAgB,kBAAA,CAAA,GAAsB,gBAAA"}
1
+ {"version":3,"file":"useHasCommittedRef.d.mts","names":[],"sources":["../src/useHasCommittedRef.ts"],"mappings":";;;;;AASA;;;;;iBAAgB,kBAAA,CAAA,GAAsB,gBAAA"}
@@ -1,12 +1,16 @@
1
- import { useEffect, useRef } from "react";
1
+ import { useInsertionEffect, useRef } from "react";
2
2
 
3
3
  //#region src/useHasCommittedRef.ts
4
4
  /**
5
5
  * Returns true if the component has committed, false otherwise.
6
+ *
7
+ * Set in an insertion effect, which React runs in the mutation phase of the commit, before any layout or passive
8
+ * effect of the commit. Layout and passive effects of the component's children, and DOM event handlers that run
9
+ * before the passive effects are flushed, therefore read true.
6
10
  */
7
11
  function useHasCommittedRef() {
8
12
  const hasCommittedRef = useRef(false);
9
- useEffect(() => {
13
+ useInsertionEffect(() => {
10
14
  hasCommittedRef.current = true;
11
15
  }, []);
12
16
  return hasCommittedRef;
@@ -1 +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"}
1
+ {"version":3,"file":"useHasCommittedRef.mjs","names":[],"sources":["../src/useHasCommittedRef.ts"],"sourcesContent":["import { type MutableRefObject, useInsertionEffect, useRef } from 'react';\n\n/**\n * Returns true if the component has committed, false otherwise.\n *\n * Set in an insertion effect, which React runs in the mutation phase of the commit, before any layout or passive\n * effect of the commit. Layout and passive effects of the component's children, and DOM event handlers that run\n * before the passive effects are flushed, therefore read true.\n */\nexport function useHasCommittedRef(): MutableRefObject<boolean> {\n const hasCommittedRef = useRef(false);\n useInsertionEffect(() => {\n hasCommittedRef.current = true;\n }, []);\n return hasCommittedRef;\n}\n"],"mappings":";;;;;;;;;;AASA,SAAgB,qBAAgD;CAC9D,MAAM,kBAAkB,OAAO,MAAM;AACrC,0BAAyB;AACvB,kBAAgB,UAAU;IACzB,EAAE,CAAC;AACN,QAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isograph/react-disposable-state",
3
- "version": "0.0.0-main-27034e7e",
3
+ "version": "0.0.0-main-977768e2",
4
4
  "description": "Primitives for managing disposable state in React",
5
5
  "homepage": "https://isograph.dev",
6
6
  "main": "./dist/index.cjs",
@@ -8,7 +8,7 @@
8
8
  "author": "Isograph Labs",
9
9
  "license": "MIT",
10
10
  "dependencies": {
11
- "@isograph/disposable-types": "0.0.0-main-27034e7e"
11
+ "@isograph/disposable-types": "0.0.0-main-977768e2"
12
12
  },
13
13
  "peerDependencies": {
14
14
  "react": "^18.0.0 || ^19.0.0"
@@ -1,11 +1,15 @@
1
- import { type MutableRefObject, useEffect, useRef } from 'react';
1
+ import { type MutableRefObject, useInsertionEffect, useRef } from 'react';
2
2
 
3
3
  /**
4
4
  * Returns true if the component has committed, false otherwise.
5
+ *
6
+ * Set in an insertion effect, which React runs in the mutation phase of the commit, before any layout or passive
7
+ * effect of the commit. Layout and passive effects of the component's children, and DOM event handlers that run
8
+ * before the passive effects are flushed, therefore read true.
5
9
  */
6
10
  export function useHasCommittedRef(): MutableRefObject<boolean> {
7
11
  const hasCommittedRef = useRef(false);
8
- useEffect(() => {
12
+ useInsertionEffect(() => {
9
13
  hasCommittedRef.current = true;
10
14
  }, []);
11
15
  return hasCommittedRef;
@@ -1,4 +1,11 @@
1
- import React, { type MutableRefObject, StrictMode } 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 {
@@ -59,8 +66,51 @@ async function awaitableCreate(Component, isConcurrent: boolean) {
59
66
  return element;
60
67
  }
61
68
 
62
- describe('nothing', () => {
63
- 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
+ );
64
114
  });
65
115
 
66
116
  // Temporarily disable unit tests until flakiness is investigated