Skip to content

Commit c2d7123

Browse files
Merge pull request #248 from preactjs/vnode_mask2
2 parents 90d92e6 + 9ec2dc9 commit c2d7123

File tree

5 files changed

+53
-26
lines changed

5 files changed

+53
-26
lines changed

.changeset/few-elephants-occur.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+
Fix vnode masks not matching with core due to top level component Fragments

package-lock.json

Lines changed: 14 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
"lint-staged": "^10.5.3",
116116
"microbundle": "^0.13.0",
117117
"mocha": "^8.2.1",
118-
"preact": "^10.5.7",
118+
"preact": "^10.11.1",
119119
"prettier": "^2.2.1",
120120
"sinon": "^9.2.2",
121121
"sinon-chai": "^3.5.0",

src/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,12 @@ function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
239239
}
240240
}
241241

242+
// When a component returns a Fragment node we flatten it in core, so we
243+
// need to mirror that logic here too
244+
let isTopLevelFragment =
245+
rendered != null && rendered.type === Fragment && rendered.key == null;
246+
rendered = isTopLevelFragment ? rendered.props.children : rendered;
247+
242248
// Recurse into children before invoking the after-diff hook
243249
const str = _renderToString(
244250
rendered,

test/render.test.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
useContext,
66
useEffect,
77
useLayoutEffect,
8-
useMemo
8+
useMemo,
9+
useId
910
} from 'preact/hooks';
1011
import { expect } from 'chai';
1112
import { spy, stub, match } from 'sinon';
@@ -1268,4 +1269,29 @@ describe('render', () => {
12681269
it('should not render function children', () => {
12691270
expect(render(<div>{() => {}}</div>)).to.equal('<div></div>');
12701271
});
1272+
1273+
describe('vnode masks (useId)', () => {
1274+
it('should skip component top level Fragment child', () => {
1275+
const Wrapper = ({ children }) => <Fragment>{children}</Fragment>;
1276+
1277+
function Foo() {
1278+
const id = useId();
1279+
return <p>{id}</p>;
1280+
}
1281+
1282+
function App() {
1283+
const id = useId();
1284+
return (
1285+
<div>
1286+
<p>{id}</p>
1287+
<Wrapper>
1288+
<Foo />
1289+
</Wrapper>
1290+
</div>
1291+
);
1292+
}
1293+
1294+
expect(render(<App />)).to.equal('<div><p>P481</p><p>P476951</p></div>');
1295+
});
1296+
});
12711297
});

0 commit comments

Comments
 (0)