Skip to content
This repository was archived by the owner on Jan 13, 2022. It is now read-only.

Commit 2790942

Browse files
author
Ben Newman
committed
Allow rendering React components without destroying sibling nodes.
This effectively removes the requirement of providing a fixed root container for React.render to use!
1 parent 8e06b58 commit 2790942

File tree

3 files changed

+44
-8
lines changed

3 files changed

+44
-8
lines changed

examples/leaderboard/leaderboard.html

+1-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
<div id="outer">
77
{{> loginButtons}}
88
<br><br><br>
9-
<!-- It's regrettable that this wrapper <div> is still necessary for
10-
React.render to have an empty container to render into. -->
11-
<div id="inner">{{> Leaderboard}}</div>
9+
{{> Leaderboard}}
1210
</div>
1311
</body>

examples/leaderboard/leaderboard.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ var Leaderboard = ReactMeteor.createClass({
8383
);
8484
}
8585

86-
return <div>{ children }</div>;
86+
return <div className="inner">{ children }</div>;
8787
}
8888
});
8989

src/ReactMeteor.js

+42-4
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,44 @@ function enqueueMeteorStateUpdate(component) {
7272
});
7373
}
7474

75+
// Like React.render, but it replaces targetNode, and works even if
76+
// targetNode.parentNode has children other than targetNode.
77+
function renderInPlaceOfNode(reactElement, targetNode) {
78+
var container = targetNode.parentNode;
79+
var prevSibs = [];
80+
var nextSibs = [];
81+
var sibs = prevSibs;
82+
var child = container.firstChild;
83+
84+
while (child) {
85+
if (child === targetNode) {
86+
sibs = nextSibs;
87+
} else {
88+
sibs.push(child);
89+
}
90+
var next = child.nextSibling;
91+
container.removeChild(child);
92+
child = next;
93+
}
94+
95+
var result = React.render(reactElement, container);
96+
var rendered = container.firstChild;
97+
98+
if (prevSibs.length > 0) {
99+
prevSibs.forEach(function(sib) {
100+
container.insertBefore(sib, rendered);
101+
});
102+
}
103+
104+
if (nextSibs.length > 0) {
105+
nextSibs.forEach(function(sib) {
106+
container.appendChild(sib);
107+
});
108+
}
109+
110+
return result;
111+
}
112+
75113
ReactMeteor = {
76114
Mixin: ReactMeteorMixin,
77115

@@ -87,17 +125,17 @@ ReactMeteor = {
87125
var template = new Template(
88126
spec.templateName,
89127
function() {
90-
// A placeholder HTML element whose parentNode will serve as the
91-
// container into which React.render renders the component.
128+
// A placeholder HTML element that will serve as the mounting
129+
// point for the React component. May have siblings!
92130
return new HTML.SPAN;
93131
}
94132
);
95133

96134
template.onRendered(function() {
97-
React.render(
135+
renderInPlaceOfNode(
98136
// Equivalent to <Cls {...this.data} />:
99137
React.createElement(Cls, this.data || {}),
100-
this.find("span").parentNode
138+
this.find("span")
101139
);
102140
});
103141

0 commit comments

Comments
 (0)