From 3c4fcbc18dc1b36fa049a58168aa7651a67fd416 Mon Sep 17 00:00:00 2001
From: jdecroock
Date: Tue, 27 May 2025 08:44:49 +0200
Subject: [PATCH 1/6] Offer Preact utils as well
---
.changeset/fast-pens-leave.md | 5 ++
karma.conf.js | 1 +
package.json | 4 +-
packages/preact/README.md | 78 +++++++++++++++++
packages/preact/package.json | 6 ++
packages/preact/src/utils.ts | 69 +++++++++++++++
packages/preact/utils/package.json | 26 ++++++
packages/preact/utils/src/index.ts | 68 +++++++++++++++
.../preact/utils/test/browser/index.test.tsx | 84 +++++++++++++++++++
packages/react/package.json | 3 +
packages/react/utils/package.json | 2 +-
.../react/utils/test/browser/index.test.tsx | 2 +-
tsconfig.json | 1 +
13 files changed, 346 insertions(+), 3 deletions(-)
create mode 100644 .changeset/fast-pens-leave.md
create mode 100644 packages/preact/src/utils.ts
create mode 100644 packages/preact/utils/package.json
create mode 100644 packages/preact/utils/src/index.ts
create mode 100644 packages/preact/utils/test/browser/index.test.tsx
diff --git a/.changeset/fast-pens-leave.md b/.changeset/fast-pens-leave.md
new file mode 100644
index 000000000..4ff0edf8b
--- /dev/null
+++ b/.changeset/fast-pens-leave.md
@@ -0,0 +1,5 @@
+---
+"@preact/signals": minor
+---
+
+Provide `@preact/signals/utils` package with some helpers to make working with signals easier in Preact
diff --git a/karma.conf.js b/karma.conf.js
index fd71eaa30..35329637d 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -227,6 +227,7 @@ const pkgList = {
"react/utils": "@preact/signals-react/utils",
"react/runtime": "@preact/signals-react/runtime",
"react-transform": "@preact/signals-react-transform",
+ "preact/utils": "@preact/signals/utils",
};
module.exports = function (config) {
diff --git a/package.json b/package.json
index 46d3e4143..05d0e5959 100644
--- a/package.json
+++ b/package.json
@@ -3,10 +3,11 @@
"private": true,
"scripts": {
"prebuild": "shx rm -rf packages/*/dist/",
- "build": "pnpm build:core && pnpm build:preact && pnpm build:react-runtime && pnpm build:react && pnpm build:react-transform && pnpm build:react-utils",
+ "build": "pnpm build:core && pnpm build:preact && pnpm build:preact-utils && pnpm build:react-runtime && pnpm build:react && pnpm build:react-transform && pnpm build:react-utils",
"_build": "microbundle --raw --globals @preact/signals-core=preactSignalsCore,preact/hooks=preactHooks,@preact/signals-react/runtime=reactSignalsRuntime",
"build:core": "pnpm _build --cwd packages/core && pnpm postbuild:core",
"build:preact": "pnpm _build --cwd packages/preact && pnpm postbuild:preact",
+ "build:preact-utils": "pnpm _build --cwd packages/preact/utils && pnpm postbuild:preact-utils",
"build:react": "pnpm _build --cwd packages/react --external \"react,@preact/signals-react/runtime,@preact/signals-core\" && pnpm postbuild:react",
"build:react-utils": "pnpm _build --cwd packages/react/utils && pnpm postbuild:react-utils",
"build:react-runtime": "pnpm _build --cwd packages/react/runtime && pnpm postbuild:react-runtime",
@@ -14,6 +15,7 @@
"postbuild:core": "cd packages/core/dist && shx mv -f index.d.ts signals-core.d.ts",
"postbuild:preact": "cd packages/preact/dist && shx mv -f preact/src/index.d.ts signals.d.ts && shx rm -rf preact",
"postbuild:react": "cd packages/react/dist && shx mv -f react/src/index.d.ts signals.d.ts && shx rm -rf react",
+ "postbuild:preact-utils": "cd packages/preact/utils/dist && shx mv -f preact/utils/src/index.d.ts . && shx rm -rf preact",
"postbuild:react-utils": "cd packages/react/utils/dist && shx mv -f react/utils/src/index.d.ts . && shx rm -rf react",
"postbuild:react-runtime": "cd packages/react/runtime/dist && shx mv -f react/runtime/src/*.d.ts . && shx rm -rf react",
"lint": "pnpm lint:eslint && pnpm lint:tsc",
diff --git a/packages/preact/README.md b/packages/preact/README.md
index 6b62d16f7..94385b714 100644
--- a/packages/preact/README.md
+++ b/packages/preact/README.md
@@ -111,6 +111,84 @@ function Person() {
This way we'll bypass checking the virtual-dom and update the DOM property directly.
+## Utility Components and Hooks
+
+The `@preact/signals/utils` package provides additional utility components and hooks to make working with signals even easier.
+
+### Show Component
+
+The `Show` component provides a declarative way to conditionally render content based on a signal's value.
+
+```js
+import { Show } from "@preact/signals-react/utils";
+import { signal } from "@preact/signals-react";
+
+const isVisible = signal(false);
+
+function App() {
+ return (
+ Nothing to see here
}>
+ Now you see me!
+
+ );
+}
+
+// You can also use a function to access the value
+function App() {
+ return {value => The value is {value}
};
+}
+```
+
+### For Component
+
+The `For` component helps you render lists from signal arrays with automatic caching of rendered items.
+
+```js
+import { For } from "@preact/signals-react/utils";
+import { signal } from "@preact/signals-react";
+
+const items = signal(["A", "B", "C"]);
+
+function App() {
+ return (
+ No items}>
+ {(item, index) => Item: {item}
}
+
+ );
+}
+```
+
+### Additional Hooks
+
+#### useLiveSignal
+
+The `useLiveSignal` hook allows you to create a local signal that stays synchronized with an external signal.
+
+```js
+import { useLiveSignal } from "@preact/signals-react/utils";
+import { signal } from "@preact/signals-react";
+
+const external = signal(0);
+
+function Component() {
+ const local = useLiveSignal(external);
+ // local will automatically update when external changes
+}
+```
+
+#### useSignalRef
+
+The `useSignalRef` hook creates a signal that behaves like a React ref with a `.current` property.
+
+```js
+import { useSignalRef } from "@preact/signals-react/utils";
+
+function Component() {
+ const ref = useSignalRef(null);
+ return The ref's value is {ref.current}
;
+}
+```
+
## License
`MIT`, see the [LICENSE](../../LICENSE) file.
diff --git a/packages/preact/package.json b/packages/preact/package.json
index 4775088bf..850f45f9b 100644
--- a/packages/preact/package.json
+++ b/packages/preact/package.json
@@ -30,6 +30,12 @@
"browser": "./dist/signals.module.js",
"import": "./dist/signals.mjs",
"require": "./dist/signals.js"
+ },
+ "./utils": {
+ "types": "./dist/utils.d.ts",
+ "browser": "./dist/utils.module.js",
+ "import": "./dist/utils.mjs",
+ "require": "./dist/utils.js"
}
},
"mangle": "../../mangle.json",
diff --git a/packages/preact/src/utils.ts b/packages/preact/src/utils.ts
new file mode 100644
index 000000000..4f8eb0c4f
--- /dev/null
+++ b/packages/preact/src/utils.ts
@@ -0,0 +1,69 @@
+import { ReadonlySignal, Signal } from "@preact/signals-core";
+import { useSignal } from "@preact/signals";
+import { Fragment, VNode, createElement } from "preact";
+import { useMemo } from "preact/hooks";
+
+interface ShowProps {
+ when: Signal | ReadonlySignal;
+ fallback?: VNode | null;
+ children: VNode | ((value: T) => VNode);
+}
+
+export function Show(props: ShowProps): VNode | null {
+ const value = props.when.value;
+ if (!value) return props.fallback || null;
+ return typeof props.children === "function"
+ ? props.children(value)
+ : props.children;
+}
+
+interface ForProps {
+ each:
+ | Signal>
+ | ReadonlySignal>
+ | (() => Signal> | ReadonlySignal>);
+ fallback?: VNode;
+ children: (value: T, index: number) => VNode;
+}
+
+export function For(props: ForProps): VNode | null {
+ const cache = useMemo(() => new Map(), []);
+ let list = (
+ (typeof props.each === "function" ? props.each() : props.each) as Signal<
+ Array
+ >
+ ).value;
+
+ if (!list.length) return props.fallback || null;
+
+ const items = list.map((value, key) => {
+ if (!cache.has(value)) {
+ cache.set(value, props.children(value, key));
+ }
+ return cache.get(value);
+ });
+ return createElement(Fragment, null, items);
+}
+
+export function useLiveSignal(value: Signal | ReadonlySignal) {
+ const s = useSignal(value);
+ if (s.peek() !== value) s.value = value;
+ return s;
+}
+
+export function useSignalRef(value: T) {
+ const ref = useSignal(value) as Signal & { current: T };
+ if (!("current" in ref))
+ Object.defineProperty(ref, "current", refSignalProto);
+ return ref;
+}
+
+const refSignalProto = {
+ configurable: true,
+ get(this: Signal) {
+ return this.value;
+ },
+ set(this: Signal, v: any) {
+ this.value = v;
+ },
+};
diff --git a/packages/preact/utils/package.json b/packages/preact/utils/package.json
new file mode 100644
index 000000000..2bacd0cf9
--- /dev/null
+++ b/packages/preact/utils/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "@preact/signals-preact-utils",
+ "description": "Sub package for @preact/signals that contains some useful utilities",
+ "private": true,
+ "amdName": "preactSignalsutils",
+ "main": "dist/utils.js",
+ "module": "dist/utils.module.js",
+ "unpkg": "dist/utils.min.js",
+ "types": "dist/index.d.ts",
+ "source": "src/index.ts",
+ "mangle": "../../../mangle.json",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "browser": "./dist/utils.module.js",
+ "import": "./dist/utils.mjs",
+ "require": "./dist/utils.js"
+ }
+ },
+ "dependencies": {
+ "@preact/signals-core": "workspace:^1.3.0"
+ },
+ "peerDependencies": {
+ "react": "^16.14.0 || 17.x || 18.x || 19.x"
+ }
+}
diff --git a/packages/preact/utils/src/index.ts b/packages/preact/utils/src/index.ts
new file mode 100644
index 000000000..d344870fb
--- /dev/null
+++ b/packages/preact/utils/src/index.ts
@@ -0,0 +1,68 @@
+import { ReadonlySignal, Signal } from "@preact/signals-core";
+import { useSignal } from "@preact/signals";
+import { Fragment, createElement, JSX } from "preact";
+import { useMemo } from "preact/hooks";
+
+interface ShowProps {
+ when: Signal | ReadonlySignal;
+ fallback?: JSX.Element;
+ children: JSX.Element | ((value: T) => JSX.Element);
+}
+
+export function Show(props: ShowProps): JSX.Element | null {
+ const value = props.when.value;
+ if (!value) return props.fallback || null;
+ return typeof props.children === "function"
+ ? props.children(value)
+ : props.children;
+}
+
+interface ForProps {
+ each:
+ | Signal>
+ | ReadonlySignal>
+ | (() => Signal> | ReadonlySignal>);
+ fallback?: JSX.Element;
+ children: (value: T, index: number) => JSX.Element;
+}
+
+export function For(props: ForProps): JSX.Element | null {
+ const cache = useMemo(() => new Map(), []);
+ let list = (
+ (typeof props.each === "function" ? props.each() : props.each) as Signal<
+ Array
+ >
+ ).value;
+
+ if (!list.length) return props.fallback || null;
+
+ const items = list.map((value, key) => {
+ if (!cache.has(value)) {
+ cache.set(value, props.children(value, key));
+ }
+ return cache.get(value);
+ });
+ return createElement(Fragment, null, items);
+}
+
+export function useLiveSignal(value: Signal | ReadonlySignal) {
+ const s = useSignal(value);
+ if (s.peek() !== value) s.value = value;
+ return s;
+}
+
+export function useSignalRef(value: T) {
+ const ref = useSignal(value) as Signal & { current: T };
+ if (!("current" in ref))
+ Object.defineProperty(ref, "current", refSignalProto);
+ return ref;
+}
+const refSignalProto = {
+ configurable: true,
+ get(this: Signal) {
+ return this.value;
+ },
+ set(this: Signal, v: any) {
+ this.value = v;
+ },
+};
diff --git a/packages/preact/utils/test/browser/index.test.tsx b/packages/preact/utils/test/browser/index.test.tsx
new file mode 100644
index 000000000..a32a62952
--- /dev/null
+++ b/packages/preact/utils/test/browser/index.test.tsx
@@ -0,0 +1,84 @@
+import { signal } from "@preact/signals";
+import { For, Show, useSignalRef } from "@preact/signals/utils";
+import { render, createElement } from "preact";
+import { act } from "preact/test-utils";
+
+describe("@preact/signals-react-utils", () => {
+ let scratch: HTMLDivElement;
+
+ beforeEach(async () => {
+ scratch = document.createElement("div");
+ document.body.appendChild(scratch);
+ });
+
+ afterEach(async () => {
+ render(null, scratch);
+ });
+
+ describe("", () => {
+ it("Should reactively show an element", async () => {
+ const toggle = signal(false)!;
+ const Paragraph = (props: any) => {props.children}
;
+ await act(() => {
+ render(
+ Hiding}>
+ Showing
+ ,
+ scratch
+ );
+ });
+ expect(scratch.innerHTML).to.eq("Hiding
");
+
+ await act(() => {
+ toggle.value = true;
+ });
+ expect(scratch.innerHTML).to.eq("Showing
");
+ });
+ });
+
+ describe("", () => {
+ it("Should iterate over a list of signals", async () => {
+ const list = signal>([])!;
+ const Paragraph = (p: any) => {p.children}
;
+ await act(() => {
+ render(
+ No items}>
+ {item => {item}}
+ ,
+ scratch
+ );
+ });
+ expect(scratch.innerHTML).to.eq("No items
");
+
+ await act(() => {
+ list.value = ["foo", "bar"];
+ });
+ expect(scratch.innerHTML).to.eq("foo
bar
");
+ });
+ });
+
+ describe("useSignalRef", () => {
+ it("should work", async () => {
+ let ref;
+ const Paragraph = (p: any) => {
+ ref = useSignalRef(null);
+ return p.type === "span" ? (
+ {p.children}
+ ) : (
+ {p.children}
+ );
+ };
+ await act(() => {
+ render(1, scratch);
+ });
+ expect(scratch.innerHTML).to.eq("1
");
+ expect((ref as any).value instanceof HTMLParagraphElement).to.eq(true);
+
+ await act(() => {
+ render(1, scratch);
+ });
+ expect(scratch.innerHTML).to.eq("1");
+ expect((ref as any).value instanceof HTMLSpanElement).to.eq(true);
+ });
+ });
+});
diff --git a/packages/react/package.json b/packages/react/package.json
index 2b8d0cd79..fcc140f2f 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -53,6 +53,9 @@
"runtime/dist",
"runtime/src",
"runtime/package.json",
+ "utils/dist",
+ "utils/src",
+ "utils/package.json",
"CHANGELOG.md",
"LICENSE",
"README.md"
diff --git a/packages/react/utils/package.json b/packages/react/utils/package.json
index 94b68c663..58aca9053 100644
--- a/packages/react/utils/package.json
+++ b/packages/react/utils/package.json
@@ -1,5 +1,5 @@
{
- "name": "@preact/signals-react-runtime",
+ "name": "@preact/signals-react-utils",
"description": "Sub package for @preact/signals-react that contains some useful utilities",
"private": true,
"amdName": "reactSignalsutils",
diff --git a/packages/react/utils/test/browser/index.test.tsx b/packages/react/utils/test/browser/index.test.tsx
index 4d50d10b1..4aa007aa4 100644
--- a/packages/react/utils/test/browser/index.test.tsx
+++ b/packages/react/utils/test/browser/index.test.tsx
@@ -1,4 +1,4 @@
-import { For, Show, useSignalRef } from "../../src";
+import { For, Show, useSignalRef } from "@preact/signals-react/utils";
import {
act,
checkHangingAct,
diff --git a/tsconfig.json b/tsconfig.json
index 2988026e8..4d3f9d15a 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -16,6 +16,7 @@
"@preact/signals": ["./packages/preact/src/index.ts"],
"@preact/signals-react": ["./packages/react/src/index.ts"],
"@preact/signals-react/utils": ["./packages/react/utils/src/index.ts"],
+ "@preact/signals/utils": ["./packages/preact/utils/src/index.ts"],
"@preact/signals-react/runtime": [
"./packages/react/runtime/src/index.ts"
],
From b94e61197d431c498be03b04c237f5629a15913c Mon Sep 17 00:00:00 2001
From: Jovi De Croock
Date: Tue, 27 May 2025 08:48:41 +0200
Subject: [PATCH 2/6] Delete packages/preact/src/utils.ts
---
packages/preact/src/utils.ts | 69 ------------------------------------
1 file changed, 69 deletions(-)
delete mode 100644 packages/preact/src/utils.ts
diff --git a/packages/preact/src/utils.ts b/packages/preact/src/utils.ts
deleted file mode 100644
index 4f8eb0c4f..000000000
--- a/packages/preact/src/utils.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import { ReadonlySignal, Signal } from "@preact/signals-core";
-import { useSignal } from "@preact/signals";
-import { Fragment, VNode, createElement } from "preact";
-import { useMemo } from "preact/hooks";
-
-interface ShowProps {
- when: Signal | ReadonlySignal;
- fallback?: VNode | null;
- children: VNode | ((value: T) => VNode);
-}
-
-export function Show(props: ShowProps): VNode | null {
- const value = props.when.value;
- if (!value) return props.fallback || null;
- return typeof props.children === "function"
- ? props.children(value)
- : props.children;
-}
-
-interface ForProps {
- each:
- | Signal>
- | ReadonlySignal>
- | (() => Signal> | ReadonlySignal>);
- fallback?: VNode;
- children: (value: T, index: number) => VNode;
-}
-
-export function For(props: ForProps): VNode | null {
- const cache = useMemo(() => new Map(), []);
- let list = (
- (typeof props.each === "function" ? props.each() : props.each) as Signal<
- Array
- >
- ).value;
-
- if (!list.length) return props.fallback || null;
-
- const items = list.map((value, key) => {
- if (!cache.has(value)) {
- cache.set(value, props.children(value, key));
- }
- return cache.get(value);
- });
- return createElement(Fragment, null, items);
-}
-
-export function useLiveSignal(value: Signal | ReadonlySignal) {
- const s = useSignal(value);
- if (s.peek() !== value) s.value = value;
- return s;
-}
-
-export function useSignalRef(value: T) {
- const ref = useSignal(value) as Signal & { current: T };
- if (!("current" in ref))
- Object.defineProperty(ref, "current", refSignalProto);
- return ref;
-}
-
-const refSignalProto = {
- configurable: true,
- get(this: Signal) {
- return this.value;
- },
- set(this: Signal, v: any) {
- this.value = v;
- },
-};
From 2fd787c56b9ea40beba7dbf7146600d8ce93c096 Mon Sep 17 00:00:00 2001
From: jdecroock
Date: Tue, 27 May 2025 08:52:11 +0200
Subject: [PATCH 3/6] Suggestions
---
packages/preact/README.md | 14 ++++++-------
packages/preact/package.json | 13 +++++++-----
packages/preact/utils/package.json | 2 +-
.../preact/utils/test/browser/index.test.tsx | 20 +++++++++----------
4 files changed, 26 insertions(+), 23 deletions(-)
diff --git a/packages/preact/README.md b/packages/preact/README.md
index 94385b714..9c303a587 100644
--- a/packages/preact/README.md
+++ b/packages/preact/README.md
@@ -120,8 +120,8 @@ The `@preact/signals/utils` package provides additional utility components and h
The `Show` component provides a declarative way to conditionally render content based on a signal's value.
```js
-import { Show } from "@preact/signals-react/utils";
-import { signal } from "@preact/signals-react";
+import { Show } from "@preact/signals/utils";
+import { signal } from "@preact/signals";
const isVisible = signal(false);
@@ -144,8 +144,8 @@ function App() {
The `For` component helps you render lists from signal arrays with automatic caching of rendered items.
```js
-import { For } from "@preact/signals-react/utils";
-import { signal } from "@preact/signals-react";
+import { For } from "@preact/signals/utils";
+import { signal } from "@preact/signals";
const items = signal(["A", "B", "C"]);
@@ -165,8 +165,8 @@ function App() {
The `useLiveSignal` hook allows you to create a local signal that stays synchronized with an external signal.
```js
-import { useLiveSignal } from "@preact/signals-react/utils";
-import { signal } from "@preact/signals-react";
+import { useLiveSignal } from "@preact/signals/utils";
+import { signal } from "@preact/signals";
const external = signal(0);
@@ -181,7 +181,7 @@ function Component() {
The `useSignalRef` hook creates a signal that behaves like a React ref with a `.current` property.
```js
-import { useSignalRef } from "@preact/signals-react/utils";
+import { useSignalRef } from "@preact/signals/utils";
function Component() {
const ref = useSignalRef(null);
diff --git a/packages/preact/package.json b/packages/preact/package.json
index 850f45f9b..63990ce5f 100644
--- a/packages/preact/package.json
+++ b/packages/preact/package.json
@@ -32,10 +32,10 @@
"require": "./dist/signals.js"
},
"./utils": {
- "types": "./dist/utils.d.ts",
- "browser": "./dist/utils.module.js",
- "import": "./dist/utils.mjs",
- "require": "./dist/utils.js"
+ "types": "./utils/dist/index.d.ts",
+ "browser": "./utils/dist/utils.module.js",
+ "import": "./utils/dist/utils.mjs",
+ "require": "./utils/dist/utils.js"
}
},
"mangle": "../../mangle.json",
@@ -44,7 +44,10 @@
"dist",
"CHANGELOG.md",
"LICENSE",
- "README.md"
+ "README.md",
+ "utils/dist",
+ "utils/package.json",
+ "utils/src"
],
"scripts": {
"prepublishOnly": "cd ../.. && pnpm build:preact"
diff --git a/packages/preact/utils/package.json b/packages/preact/utils/package.json
index 2bacd0cf9..faabf4443 100644
--- a/packages/preact/utils/package.json
+++ b/packages/preact/utils/package.json
@@ -1,5 +1,5 @@
{
- "name": "@preact/signals-preact-utils",
+ "name": "@preact/signals-utils",
"description": "Sub package for @preact/signals that contains some useful utilities",
"private": true,
"amdName": "preactSignalsutils",
diff --git a/packages/preact/utils/test/browser/index.test.tsx b/packages/preact/utils/test/browser/index.test.tsx
index a32a62952..dc1653634 100644
--- a/packages/preact/utils/test/browser/index.test.tsx
+++ b/packages/preact/utils/test/browser/index.test.tsx
@@ -3,7 +3,7 @@ import { For, Show, useSignalRef } from "@preact/signals/utils";
import { render, createElement } from "preact";
import { act } from "preact/test-utils";
-describe("@preact/signals-react-utils", () => {
+describe("@preact/signals-utils", () => {
let scratch: HTMLDivElement;
beforeEach(async () => {
@@ -16,10 +16,10 @@ describe("@preact/signals-react-utils", () => {
});
describe("", () => {
- it("Should reactively show an element", async () => {
+ it("Should reactively show an element", () => {
const toggle = signal(false)!;
const Paragraph = (props: any) => {props.children}
;
- await act(() => {
+ act(() => {
render(
Hiding}>
Showing
@@ -29,7 +29,7 @@ describe("@preact/signals-react-utils", () => {
});
expect(scratch.innerHTML).to.eq("Hiding
");
- await act(() => {
+ act(() => {
toggle.value = true;
});
expect(scratch.innerHTML).to.eq("Showing
");
@@ -37,10 +37,10 @@ describe("@preact/signals-react-utils", () => {
});
describe("", () => {
- it("Should iterate over a list of signals", async () => {
+ it("Should iterate over a list of signals", () => {
const list = signal>([])!;
const Paragraph = (p: any) => {p.children}
;
- await act(() => {
+ act(() => {
render(
No items}>
{item => {item}}
@@ -50,7 +50,7 @@ describe("@preact/signals-react-utils", () => {
});
expect(scratch.innerHTML).to.eq("No items
");
- await act(() => {
+ act(() => {
list.value = ["foo", "bar"];
});
expect(scratch.innerHTML).to.eq("foo
bar
");
@@ -58,7 +58,7 @@ describe("@preact/signals-react-utils", () => {
});
describe("useSignalRef", () => {
- it("should work", async () => {
+ it("should work", () => {
let ref;
const Paragraph = (p: any) => {
ref = useSignalRef(null);
@@ -68,13 +68,13 @@ describe("@preact/signals-react-utils", () => {
{p.children}
);
};
- await act(() => {
+ act(() => {
render(1, scratch);
});
expect(scratch.innerHTML).to.eq("1
");
expect((ref as any).value instanceof HTMLParagraphElement).to.eq(true);
- await act(() => {
+ act(() => {
render(1, scratch);
});
expect(scratch.innerHTML).to.eq("1");
From 3fad2d9d2767d8fbb8744a70bc5e9144faa7aaf4 Mon Sep 17 00:00:00 2001
From: Jovi De Croock
Date: Tue, 27 May 2025 09:16:59 +0200
Subject: [PATCH 4/6] Update packages/preact/utils/package.json
Co-authored-by: Ryan Christian <33403762+rschristian@users.noreply.github.com>
---
packages/preact/utils/package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/preact/utils/package.json b/packages/preact/utils/package.json
index faabf4443..86d868c80 100644
--- a/packages/preact/utils/package.json
+++ b/packages/preact/utils/package.json
@@ -21,6 +21,6 @@
"@preact/signals-core": "workspace:^1.3.0"
},
"peerDependencies": {
- "react": "^16.14.0 || 17.x || 18.x || 19.x"
+ "preact": "10.x"
}
}
From c2c962232e509c37c01c88c4d9d0b346245fe7d6 Mon Sep 17 00:00:00 2001
From: jdecroock
Date: Tue, 27 May 2025 09:18:10 +0200
Subject: [PATCH 5/6] Correctly inherit peerDep
---
packages/preact/utils/package.json | 2 +-
packages/preact/utils/src/index.ts | 7 +++++--
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/packages/preact/utils/package.json b/packages/preact/utils/package.json
index 86d868c80..bf676d46a 100644
--- a/packages/preact/utils/package.json
+++ b/packages/preact/utils/package.json
@@ -21,6 +21,6 @@
"@preact/signals-core": "workspace:^1.3.0"
},
"peerDependencies": {
- "preact": "10.x"
+ "preact": ">= 10.25.0"
}
}
diff --git a/packages/preact/utils/src/index.ts b/packages/preact/utils/src/index.ts
index d344870fb..98478882d 100644
--- a/packages/preact/utils/src/index.ts
+++ b/packages/preact/utils/src/index.ts
@@ -42,16 +42,19 @@ export function For(props: ForProps): JSX.Element | null {
}
return cache.get(value);
});
+
return createElement(Fragment, null, items);
}
-export function useLiveSignal(value: Signal | ReadonlySignal) {
+export function useLiveSignal(
+ value: Signal | ReadonlySignal
+): Signal | ReadonlySignal> {
const s = useSignal(value);
if (s.peek() !== value) s.value = value;
return s;
}
-export function useSignalRef(value: T) {
+export function useSignalRef(value: T): Signal & { current: T } {
const ref = useSignal(value) as Signal & { current: T };
if (!("current" in ref))
Object.defineProperty(ref, "current", refSignalProto);
From 430161d7cdd2d076a77cd6ccb738c6d2ef4ffb80 Mon Sep 17 00:00:00 2001
From: jdecroock
Date: Tue, 27 May 2025 09:24:58 +0200
Subject: [PATCH 6/6] Add parent
---
packages/preact/utils/package.json | 1 +
packages/react/utils/package.json | 1 +
2 files changed, 2 insertions(+)
diff --git a/packages/preact/utils/package.json b/packages/preact/utils/package.json
index bf676d46a..d1f77edb5 100644
--- a/packages/preact/utils/package.json
+++ b/packages/preact/utils/package.json
@@ -21,6 +21,7 @@
"@preact/signals-core": "workspace:^1.3.0"
},
"peerDependencies": {
+ "@preact/signals": "workspace:*",
"preact": ">= 10.25.0"
}
}
diff --git a/packages/react/utils/package.json b/packages/react/utils/package.json
index 58aca9053..0a832e1dd 100644
--- a/packages/react/utils/package.json
+++ b/packages/react/utils/package.json
@@ -21,6 +21,7 @@
"@preact/signals-core": "workspace:^1.3.0"
},
"peerDependencies": {
+ "@preact/signals-react": "workspace:*",
"react": "^16.14.0 || 17.x || 18.x || 19.x"
}
}