Skip to content

Commit 7cd386a

Browse files
authored
Users/benfeely/bug react destroy (#6)
* Fix console error. * Fix a performance bug where React elements are not unloaded from memory. * Update to docs for github hosting.
1 parent d91bfee commit 7cd386a

File tree

7 files changed

+92
-19
lines changed

7 files changed

+92
-19
lines changed

apps/docs/src/app/containers/performance/angular-perf/angular-perf.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,5 @@ <h3>Pure Angular View</h3>
2828
{{ text }}
2929
</app-dot>
3030
</ng-template>
31-
<div class="message mat-h1">{{ triangle.dots.length }} Dots</div>
31+
<div class="message mat-h1">{{ triangle.dots?.length }} Dots</div>
3232
</app-triangle>

apps/docs/src/app/containers/performance/mixed-perf/mixed-perf.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,5 @@ <h3>React Rendered Dots</h3>
5353
{{ projectAsAngular ? dot.textOverride ? dot.textOverride : text : '' }}
5454
</app-react-dot>
5555
</ng-template>
56-
<div class="message mat-h1">{{ triangle.dots.length }} Dots</div>
56+
<div class="message mat-h1">{{ triangle.dots?.length }} Dots</div>
5757
</app-triangle>

docs/3rdpartylicenses.txt

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,53 @@
22
MIT
33
MIT
44

5+
cache-loader@1.2.2
6+
MIT
7+
Copyright JS Foundation and other contributors
8+
9+
Permission is hereby granted, free of charge, to any person obtaining
10+
a copy of this software and associated documentation files (the
11+
'Software'), to deal in the Software without restriction, including
12+
without limitation the rights to use, copy, modify, merge, publish,
13+
distribute, sublicense, and/or sell copies of the Software, and to
14+
permit persons to whom the Software is furnished to do so, subject to
15+
the following conditions:
16+
17+
The above copyright notice and this permission notice shall be
18+
included in all copies or substantial portions of the Software.
19+
20+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
21+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27+
28+
@angular-devkit/build-optimizer@0.3.2
29+
MIT
30+
The MIT License
31+
32+
Copyright (c) 2017 Google, Inc.
33+
34+
Permission is hereby granted, free of charge, to any person obtaining a copy
35+
of this software and associated documentation files (the "Software"), to deal
36+
in the Software without restriction, including without limitation the rights
37+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
38+
copies of the Software, and to permit persons to whom the Software is
39+
furnished to do so, subject to the following conditions:
40+
41+
The above copyright notice and this permission notice shall be included in all
42+
copies or substantial portions of the Software.
43+
44+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
47+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
49+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
50+
SOFTWARE.
51+
552
@microsoft/load-themed-styles@1.7.50
653
MIT
754
@microsoft/load-themed-styles

docs/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@
88
// We need to redirect to the URL the user was trying to access.
99
history.replaceState(null, null, redirect);
1010
}
11-
})();</script><!-- /Github Pages hack. --><app-root class="mat-typography"></app-root><script type="text/javascript" src="inline.318b50c57b4eba3d437b.bundle.js"></script><script type="text/javascript" src="polyfills.9a8df1a7a1c2b9447f3a.bundle.js"></script><script type="text/javascript" src="main.f8b904cca0bc36731f17.bundle.js"></script></body></html>
11+
})();</script><!-- /Github Pages hack. --><app-root class="mat-typography"></app-root><script type="text/javascript" src="inline.318b50c57b4eba3d437b.bundle.js"></script><script type="text/javascript" src="polyfills.9a8df1a7a1c2b9447f3a.bundle.js"></script><script type="text/javascript" src="main.fe1da865f49a2bc3bb21.bundle.js"></script></body></html>
Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libs/core/src/renderer/react-node.ts

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export class ReactNode {
3131
return this.renderedDomElement;
3232
}
3333

34-
private _parent: any;
34+
private _parent: HTMLElement | ReactNode;
3535
set parent(parent: HTMLElement | ReactNode) {
3636
this._parent = parent;
3737
this.setRenderPending();
@@ -42,16 +42,26 @@ export class ReactNode {
4242
return !this.isNotRenderable;
4343
}
4444

45+
private isDestroyPending = false;
46+
get destroyPending() {
47+
return this.isDestroyPending;
48+
}
49+
4550
private isRenderPending = true;
46-
public setRenderPendingCallback = () => null;
51+
setRenderPendingCallback = () => null;
4752
// Track all pending render operations internally and set flag on
4853
// renderer factory so that a flush operation can be scheduled for
4954
// the "end" of render.
50-
public setRenderPending() {
55+
setRenderPending() {
5156
this.setRenderPendingCallback();
5257
this.isRenderPending = true;
5358
}
5459

60+
destroyNode() {
61+
this.setRenderPending();
62+
this.isDestroyPending = true;
63+
}
64+
5565
private tryResolveTypeIsReactElementClass() {
5666
if (this.typeIsReactElementClass === undefined) {
5767
// Comments and text have no type.
@@ -102,9 +112,7 @@ export class ReactNode {
102112
this.children = this.children.filter(child => child !== node);
103113
}
104114

105-
constructor(
106-
private type?: ReactComponentClass | string
107-
) {
115+
constructor(private type?: ReactComponentClass | string) {
108116
this.setRenderPending();
109117
this.tryResolveTypeIsReactElementClass();
110118
}
@@ -130,14 +138,27 @@ export class ReactNode {
130138
return this;
131139
}
132140

133-
render() {
134-
if (this.isRenderPending) {
135-
if (DEBUG) { console.log('ReactNode > render > node:', this.toString(), 'parent:', this.parent); }
136-
// It is expected that the element will be recreated and rerendered with each attribute change.
137-
// See: https://reactjs.org/docs/rendering-elements.html
138-
ReactDOM.render(this.renderRecursive() as any, this._parent);
139-
this.isRenderPending = false;
141+
render(): ReactNode {
142+
// Only complete render operations for ReactNodes that are parented by HTMLElements.
143+
// Those that are parented by other ReactNodes will be rendered recursively by their
144+
// parent.
145+
if (!isReactNode(this._parent)) {
146+
if (this.isDestroyPending && this._parent) {
147+
if (DEBUG) { console.log('ReactNode > render > destroy > node:', this.toString(), 'parent:', this.parent); }
148+
ReactDOM.unmountComponentAtNode(this._parent);
149+
return this;
150+
}
151+
152+
if (this.isRenderPending) {
153+
if (DEBUG) { console.log('ReactNode > render > node:', this.toString(), 'parent:', this.parent); }
154+
// It is expected that the element will be recreated and rerendered with each attribute change.
155+
// See: https://reactjs.org/docs/rendering-elements.html
156+
ReactDOM.render(this.renderRecursive() as any, this._parent);
157+
this.isRenderPending = false;
158+
}
140159
}
160+
161+
return this;
141162
}
142163

143164
private renderRecursive(): React.ReactElement<{}> | string {

libs/core/src/renderer/renderer.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,25 @@ export class AngularReactRendererFactory extends ɵDomRendererFactory2 {
6363
// earlier (as is done for DOM elements) because React element props
6464
// are ReadOnly.
6565
if (this.isRenderPending) {
66-
this.reactRootNodes.map(node => node.render());
66+
// Remove root nodes that are pending destroy after render.
67+
this.reactRootNodes = this.reactRootNodes.filter(node => !node.render().destroyPending);
6768
this.isRenderPending = false;
6869
}
6970
}
7071
}
7172

7273
class ReactRenderer implements Renderer2 {
7374
data: { [key: string]: any } = Object.create(null);
74-
destroyNode: null;
7575

7676
constructor(private rootRenderer: AngularReactRendererFactory) {}
7777

7878
destroy(): void {}
7979

80+
destroyNode(node: ReactNode): void {
81+
if (DEBUG) { console.error('Renderer > destroyNode > node:', node.toString()); }
82+
node.destroyNode();
83+
}
84+
8085
createElement(name: string, namespace?: string): ReactNode {
8186
if (DEBUG) { console.error('Renderer > createElement > name:', name, namespace ? 'namespace:' : '', namespace); }
8287
return new ReactNode(name);

0 commit comments

Comments
 (0)