Skip to content

Commit a47080d

Browse files
committed
Backport changes from #237 (child/parent properties, simplified Fragment handling)
1 parent 39f7c68 commit a47080d

File tree

2 files changed

+72
-53
lines changed

2 files changed

+72
-53
lines changed

src/constants.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export const SKIP_EFFECTS = '__s';
77

88
// VNode properties
99
export const COMPONENT = '__c';
10+
export const CHILDREN = '__k';
11+
export const PARENT = '__';
1012

1113
// Component properties
1214
export const VNODE = '__v';

src/index.js

Lines changed: 70 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { encodeEntities, styleObjToCss, UNSAFE_NAME, XLINK } from './util';
2-
import { options, Fragment } from 'preact';
2+
import { options, h, Fragment } from 'preact';
33
import {
4+
CHILDREN,
45
COMMIT,
56
COMPONENT,
67
DIFF,
78
DIFFED,
89
DIRTY,
910
NEXT_STATE,
11+
PARENT,
1012
RENDER,
1113
SKIP_EFFECTS,
1214
VNODE
@@ -41,8 +43,11 @@ export default function renderToString(vnode, context) {
4143
afterDiff = options[DIFFED];
4244
renderHook = options[RENDER];
4345

46+
const parent = h(Fragment, null);
47+
parent[CHILDREN] = [vnode];
48+
4449
try {
45-
return _renderToString(vnode, context || {}, false, undefined);
50+
return _renderToString(vnode, context || {}, false, undefined, parent);
4651
} finally {
4752
// options._commit, we don't schedule any effects in this library right now,
4853
// so we can pass an empty queue to this hook.
@@ -107,9 +112,10 @@ function renderClassComponent(vnode, context) {
107112
* @param {any} context
108113
* @param {boolean} isSvgMode
109114
* @param {any} selectValue
115+
* @param {VNode} parent
110116
* @returns {string}
111117
*/
112-
function _renderToString(vnode, context, isSvgMode, selectValue) {
118+
function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
113119
// Ignore non-rendered VNodes/values
114120
if (vnode == null || vnode === true || vnode === false || vnode === '') {
115121
return '';
@@ -123,75 +129,85 @@ function _renderToString(vnode, context, isSvgMode, selectValue) {
123129
// Recurse into children / Arrays
124130
if (isArray(vnode)) {
125131
let rendered = '';
132+
parent[CHILDREN] = vnode;
126133
for (let i = 0; i < vnode.length; i++) {
127134
rendered =
128-
rendered + _renderToString(vnode[i], context, isSvgMode, selectValue);
135+
rendered +
136+
_renderToString(vnode[i], context, isSvgMode, selectValue, parent);
129137
}
130138
return rendered;
131139
}
132140

141+
vnode[PARENT] = parent;
133142
if (beforeDiff) beforeDiff(vnode);
134143

135144
let type = vnode.type,
136-
props = vnode.props;
145+
props = vnode.props,
146+
cctx = context,
147+
contextType,
148+
rendered,
149+
component;
137150

138151
// Invoke rendering on Components
139-
const isComponent = typeof type === 'function';
152+
let isComponent = typeof type === 'function';
140153
if (isComponent) {
141154
if (type === Fragment) {
142-
return _renderToString(props.children, context, isSvgMode, selectValue);
143-
}
144-
145-
let cctx = context;
146-
let cxType = type.contextType;
147-
if (cxType != null) {
148-
let provider = context[cxType.__c];
149-
cctx = provider ? provider.props.value : cxType.__;
150-
}
151-
152-
let rendered;
153-
let component;
154-
if (type.prototype && typeof type.prototype.render === 'function') {
155-
rendered = /**#__NOINLINE__**/ renderClassComponent(vnode, cctx);
156-
component = vnode[COMPONENT];
155+
rendered = props.children;
157156
} else {
158-
component = {
159-
__v: vnode,
160-
props,
161-
context: cctx,
162-
// silently drop state updates
163-
setState: markAsDirty,
164-
forceUpdate: markAsDirty,
165-
__d: true,
166-
// hooks
167-
__h: []
168-
};
169-
vnode[COMPONENT] = component;
170-
171-
// If a hook invokes setState() to invalidate the component during rendering,
172-
// re-render it up to 25 times to allow "settling" of memoized states.
173-
// Note:
174-
// This will need to be updated for Preact 11 to use internal.flags rather than component._dirty:
175-
// https://github.com/preactjs/preact/blob/d4ca6fdb19bc715e49fd144e69f7296b2f4daa40/src/diff/component.js#L35-L44
176-
// let renderHook = options[RENDER];
177-
let count = 0;
178-
while (component[DIRTY] && count++ < 25) {
179-
component[DIRTY] = false;
180-
181-
if (renderHook) renderHook(vnode);
182-
183-
rendered = type.call(component, props, cctx);
157+
contextType = type.contextType;
158+
if (contextType != null) {
159+
let provider = context[contextType.__c];
160+
cctx = provider ? provider.props.value : contextType.__;
184161
}
185-
component[DIRTY] = true;
186-
}
187162

188-
if (component.getChildContext != null) {
189-
context = assign({}, context, component.getChildContext());
163+
if (type.prototype && typeof type.prototype.render === 'function') {
164+
rendered = /**#__NOINLINE__**/ renderClassComponent(vnode, cctx);
165+
component = vnode[COMPONENT];
166+
} else {
167+
component = {
168+
__v: vnode,
169+
props,
170+
context: cctx,
171+
// silently drop state updates
172+
setState: markAsDirty,
173+
forceUpdate: markAsDirty,
174+
__d: true,
175+
// hooks
176+
__h: []
177+
};
178+
vnode[COMPONENT] = component;
179+
180+
// If a hook invokes setState() to invalidate the component during rendering,
181+
// re-render it up to 25 times to allow "settling" of memoized states.
182+
// Note:
183+
// This will need to be updated for Preact 11 to use internal.flags rather than component._dirty:
184+
// https://github.com/preactjs/preact/blob/d4ca6fdb19bc715e49fd144e69f7296b2f4daa40/src/diff/component.js#L35-L44
185+
let count = 0;
186+
while (component[DIRTY] && count++ < 25) {
187+
component[DIRTY] = false;
188+
189+
if (renderHook) renderHook(vnode);
190+
191+
rendered = type.call(component, props, cctx);
192+
}
193+
component[DIRTY] = true;
194+
}
195+
196+
if (component.getChildContext != null) {
197+
context = assign({}, context, component.getChildContext());
198+
}
190199
}
191200

192201
// Recurse into children before invoking the after-diff hook
193-
const str = _renderToString(rendered, context, isSvgMode, selectValue);
202+
const str = _renderToString(
203+
rendered,
204+
context,
205+
isSvgMode,
206+
selectValue,
207+
vnode
208+
);
194209
if (afterDiff) afterDiff(vnode);
210+
vnode[PARENT] = undefined;
195211
return str;
196212
}
197213

@@ -302,10 +318,11 @@ function _renderToString(vnode, context, isSvgMode, selectValue) {
302318
// recurse into this element VNode's children
303319
let childSvgMode =
304320
type === 'svg' || (type !== 'foreignObject' && isSvgMode);
305-
html = _renderToString(children, context, childSvgMode, selectValue);
321+
html = _renderToString(children, context, childSvgMode, selectValue, vnode);
306322
}
307323

308324
if (afterDiff) afterDiff(vnode);
325+
vnode[PARENT] = undefined;
309326

310327
// Emit self-closing tag for empty void elements:
311328
if (!html) {

0 commit comments

Comments
 (0)