Skip to content

Commit ec0b614

Browse files
authored
Support new dirty bit (#423)
1 parent d424e9c commit ec0b614

File tree

6 files changed

+57
-14
lines changed

6 files changed

+57
-14
lines changed

.changeset/tiny-buttons-grin.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'preact-render-to-string': patch
3+
---
4+
5+
Add support for new component bits

src/index.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import {
66
HTML_LOWER_CASE,
77
HTML_ENUMERATED,
88
SVG_CAMEL_CASE,
9-
createComponent
9+
createComponent,
10+
setDirty,
11+
unsetDirty,
12+
isDirty
1013
} from './lib/util.js';
1114
import { options, h, Fragment } from 'preact';
1215
import {
@@ -15,7 +18,6 @@ import {
1518
COMPONENT,
1619
DIFF,
1720
DIFFED,
18-
DIRTY,
1921
NEXT_STATE,
2022
PARENT,
2123
RENDER,
@@ -174,8 +176,9 @@ function renderClassComponent(vnode, context) {
174176

175177
c.props = vnode.props;
176178
c.context = context;
177-
// turn off stateful re-rendering:
178-
c[DIRTY] = true;
179+
180+
// Turn off stateful rendering
181+
setDirty(c);
179182

180183
if (c.state == null) c.state = EMPTY_OBJ;
181184

@@ -370,8 +373,8 @@ function _renderToString(
370373
// This will need to be updated for Preact 11 to use internal.flags rather than component._dirty:
371374
// https://github.com/preactjs/preact/blob/d4ca6fdb19bc715e49fd144e69f7296b2f4daa40/src/diff/component.js#L35-L44
372375
let count = 0;
373-
while (component[DIRTY] && count++ < 25) {
374-
component[DIRTY] = false;
376+
while (isDirty(component) && count++ < 25) {
377+
unsetDirty(component);
375378

376379
if (renderHook) renderHook(vnode);
377380

@@ -384,7 +387,8 @@ function _renderToString(
384387
throw e;
385388
}
386389
}
387-
component[DIRTY] = true;
390+
391+
setDirty(component);
388392
}
389393

390394
if (component.getChildContext != null) {
@@ -425,7 +429,7 @@ function _renderToString(
425429
component.componentDidCatch(err, EMPTY_OBJ);
426430
}
427431

428-
if (component[DIRTY]) {
432+
if (isDirty(component)) {
429433
rendered = renderClassComponent(vnode, context);
430434
component = vnode[COMPONENT];
431435

src/lib/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ export const MASK = '__m';
1515
// Component properties
1616
export const VNODE = '__v';
1717
export const DIRTY = '__d';
18+
export const BITS = '__g';
1819
export const NEXT_STATE = '__s';
1920
export const CHILD_DID_SUSPEND = '__c';

src/lib/util.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { DIRTY, BITS } from './constants';
2+
13
export const VOID_ELEMENTS = /^(?:area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/;
24
// oxlint-disable-next-line no-control-regex
35
export const UNSAFE_NAME = /[\s\n\\/='"\0<>]/;
@@ -8,6 +10,30 @@ export const SVG_CAMEL_CASE = /^ac|^ali|arabic|basel|cap|clipPath$|clipRule$|col
810
// Boolean DOM properties that translate to enumerated ('true'/'false') attributes
911
export const HTML_ENUMERATED = new Set(['draggable', 'spellcheck']);
1012

13+
export const COMPONENT_DIRTY_BIT = 1 << 3;
14+
export function setDirty(component) {
15+
if (component[BITS] !== undefined) {
16+
component[BITS] |= COMPONENT_DIRTY_BIT;
17+
} else {
18+
component[DIRTY] = true;
19+
}
20+
}
21+
22+
export function unsetDirty(component) {
23+
if (component.__g !== undefined) {
24+
component.__g &= ~COMPONENT_DIRTY_BIT;
25+
} else {
26+
component[DIRTY] = false;
27+
}
28+
}
29+
30+
export function isDirty(component) {
31+
if (component.__g !== undefined) {
32+
return (component.__g & COMPONENT_DIRTY_BIT) !== 0;
33+
}
34+
return component[DIRTY] === true;
35+
}
36+
1137
// DOM properties that should NOT have "px" added when numeric
1238
const ENCODED_ENTITIES = /["&<]/;
1339

src/pretty.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ import {
1010
NAMESPACE_REPLACE_REGEX,
1111
SVG_CAMEL_CASE,
1212
HTML_LOWER_CASE,
13-
getContext
13+
getContext,
14+
setDirty,
15+
isDirty,
16+
unsetDirty
1417
} from './lib/util.js';
1518
import { COMMIT, DIFF, DIFFED, RENDER, SKIP_EFFECTS } from './lib/constants.js';
1619
import { options, Fragment } from 'preact';
@@ -139,8 +142,8 @@ function _renderToStringPretty(
139142
// This will need to be updated for Preact 11 to use internal.flags rather than component._dirty:
140143
// https://github.com/preactjs/preact/blob/d4ca6fdb19bc715e49fd144e69f7296b2f4daa40/src/diff/component.js#L35-L44
141144
let count = 0;
142-
while (c.__d && count++ < 25) {
143-
c.__d = false;
145+
while (isDirty(c) && count++ < 25) {
146+
unsetDirty(c);
144147

145148
if (renderHook) renderHook(vnode);
146149

@@ -154,7 +157,7 @@ function _renderToStringPretty(
154157
c = vnode.__c = new nodeName(props, cctx);
155158
c.__v = vnode;
156159
// turn off stateful re-rendering:
157-
c._dirty = c.__d = true;
160+
setDirty(c);
158161
c.props = props;
159162
if (c.state == null) c.state = {};
160163

test/render.test.jsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from 'preact/hooks';
2020
import { expect, vi, describe, it } from 'vitest';
2121
import { svgAttributes, htmlAttributes } from './utils.jsx';
22+
import { COMPONENT_DIRTY_BIT } from '../src/lib/util.js';
2223

2324
function shallowRender(vnode) {
2425
const context = {};
@@ -942,8 +943,11 @@ describe('render', () => {
942943

943944
expect(render(<Foo />)).to.equal('<div></div>');
944945

945-
// expect(inst).to.have.property('_dirty', true);
946-
expect(inst).to.have.property('__d', true);
946+
if (inst.__g) {
947+
expect(!!(inst.__g & COMPONENT_DIRTY_BIT)).to.be.true;
948+
} else {
949+
expect(inst).to.have.property('__d', true);
950+
}
947951
});
948952

949953
it('should prevent re-rendering', () => {

0 commit comments

Comments
 (0)