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" } }