Skip to content

Commit aa02ea6

Browse files
committed
Add support for shallow rendering via render({ shallow:true }) or renderShallow(), and export render() as a named method (in addition to being the default export).
1 parent 54426e2 commit aa02ea6

File tree

1 file changed

+42
-14
lines changed

1 file changed

+42
-14
lines changed

src/index.js

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,29 @@ const ESC = {
77
'&': '&'
88
};
99

10+
const EMPTY = {};
11+
1012
const HOP = Object.prototype.hasOwnProperty;
1113

1214
let escape = s => String(s).replace(/[<>"&]/, a => ESC[a] || a);
1315

14-
export default function renderToString(vnode) {
16+
/** Convert JSX to a string, rendering out all nested components along the way.
17+
* @param {VNode} vnode A VNode, generally created via JSX
18+
* @param {Object} [options] Options for the renderer
19+
* @param {Boolean} [options.shallow=false] Passing `true` stops at Component VNodes without rendering them. Note: a component located at the root will always be rendered.
20+
*/
21+
export function render(vnode, opts) {
22+
return internalRender(vnode, opts || EMPTY, true);
23+
}
24+
25+
export function shallowRender(vnode, opts) {
26+
return internalRender(vnode, { shallow:true, ...(opts || EMPTY) }, true);
27+
}
28+
29+
export default render;
30+
31+
32+
function internalRender(vnode, opts, root) {
1533
let { nodeName, attributes, children } = vnode || EMPTY;
1634

1735
// #text nodes
@@ -21,20 +39,26 @@ export default function renderToString(vnode) {
2139

2240
// components
2341
if (typeof nodeName==='function') {
24-
let props = { children, ...attributes },
25-
rendered;
26-
27-
if (typeof nodeName.prototype.render!=='function') {
28-
// stateless functional components
29-
rendered = nodeName(props);
42+
if (opts.shallow===true && !root) {
43+
nodeName = getComponentName(nodeName);
3044
}
3145
else {
32-
// class-based components
33-
let c = new nodeName();
34-
c.setProps(props, NO_RENDER);
35-
rendered = c.render(c.props = props, c.state);
46+
let props = { children, ...attributes },
47+
rendered;
48+
49+
if (typeof nodeName.prototype.render!=='function') {
50+
// stateless functional components
51+
rendered = nodeName(props);
52+
}
53+
else {
54+
// class-based components
55+
let c = new nodeName();
56+
c.setProps(props, NO_RENDER);
57+
rendered = c.render(c.props = props, c.state);
58+
}
59+
60+
return internalRender(rendered, opts, false);
3661
}
37-
return renderToString(rendered);
3862
}
3963

4064
// render JSX to HTML
@@ -53,8 +77,12 @@ export default function renderToString(vnode) {
5377
}
5478
s += '>';
5579
if (children && children.length) {
56-
s += children.map(renderToString).join('');
80+
s += children.map( child => internalRender(child, opts, false) ).join('');
5781
}
5882
s += `</${nodeName}>`
5983
return s;
60-
};
84+
}
85+
86+
function getComponentName(component) {
87+
return component.displayName || component.name || component.prototype.displayName || component.prototype.name || (Function.prototype.toString.call(component).match(/\s([^\(]+)/) || EMPTY)[1] || 'Component';
88+
}

0 commit comments

Comments
 (0)