Skip to content

Commit 1422baf

Browse files
Merge pull request #147 from preactjs/preact-faster-signal-text
Improve Signal->Text rendering in Preact
2 parents 03f72fd + 2482e7e commit 1422baf

File tree

3 files changed

+29
-19
lines changed

3 files changed

+29
-19
lines changed

.changeset/popular-avocados-sip.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@preact/signals": patch
3+
"@preact/signals-react": patch
4+
---
5+
6+
Improve performance when rendering Signals as Text in Preact.

packages/preact/src/index.ts

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { options, Component, createElement } from "preact";
1+
import { options, Component } from "preact";
22
import { useRef, useMemo } from "preact/hooks";
33
import {
44
signal,
@@ -92,18 +92,6 @@ function getElementUpdater(vnode: VNode) {
9292
// return false;
9393
// }
9494

95-
/** Convert Signals within (nested) props.children into Text components */
96-
function childToSignal<T>(child: any, i: keyof T, arr: T) {
97-
if (typeof child !== "object" || child == null) {
98-
// can't be a signal
99-
} else if (Array.isArray(child)) {
100-
child.forEach(childToSignal);
101-
} else if (child instanceof Signal) {
102-
// @ts-ignore-next-line yes, arr can accept VNodes:
103-
arr[i] = createElement(Text, { data: child });
104-
}
105-
}
106-
10795
/**
10896
* A wrapper component that renders a Signal directly as a Text node.
10997
* @todo: in Preact 11, just decorate Signal with `type:null`
@@ -142,6 +130,21 @@ function Text(this: ComponentType, { data }: { data: Signal }) {
142130
}
143131
Text.displayName = "_st";
144132

133+
Object.defineProperties(Signal.prototype, {
134+
constructor: { configurable: true },
135+
type: { configurable: true, value: Text },
136+
props: {
137+
configurable: true,
138+
get() {
139+
return { data: this };
140+
},
141+
},
142+
// Setting a VNode's _depth to 1 forces Preact to clone it before modifying:
143+
// https://github.com/preactjs/preact/blob/d7a433ee8463a7dc23a05111bb47de9ec729ad4d/src/diff/children.js#L77
144+
// @todo remove this for Preact 11
145+
__b: { configurable: true, value: 1 },
146+
});
147+
145148
/** Inject low-level property/attribute bindings for Signals into Preact's diff */
146149
hook(OptionsTypes.DIFF, (old, vnode) => {
147150
if (typeof vnode.type === "string") {
@@ -151,9 +154,9 @@ hook(OptionsTypes.DIFF, (old, vnode) => {
151154

152155
for (let i in props) {
153156
let value = props[i];
154-
if (i === "children") {
155-
childToSignal(value, "children", props);
156-
} else if (value instanceof Signal) {
157+
if (i === "children") continue;
158+
159+
if (value instanceof Signal) {
157160
// first Signal prop triggers creation/cleanup of the updater:
158161
if (!updater) updater = getElementUpdater(vnode);
159162
// track which props are Signals for precise updates:

packages/react/src/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,15 @@ function Text({ data }: { data: Signal }) {
8080
//@ts-ignore-next-line
8181
const $$typeof = createElement("a").$$typeof;
8282
Object.defineProperties(Signal.prototype, {
83-
$$typeof: { value: $$typeof },
84-
type: { value: Text },
83+
$$typeof: { configurable: true, value: $$typeof },
84+
type: { configurable: true, value: Text },
8585
props: {
86+
configurable: true,
8687
get() {
8788
return { data: this };
8889
},
8990
},
90-
ref: { value: null },
91+
ref: { configurable: true, value: null },
9192
});
9293

9394
// Track the current owner (roughly equiv to current vnode)

0 commit comments

Comments
 (0)