Skip to content

Commit a4eb74c

Browse files
crisbetoAndrewKushnir
authored andcommitted
fix(core): animation sometimes renderer not being destroyed during HMR (#59574)
These changes aim to resolve the issue that prompted #59514. The animations module is a bit tricky for HMR, because it schedules the destruction of its renderer after the currently-running animations are done. If there are no running animations, the renderer gets destroyed next time around. This is a problem, because it means that the styles can stay around for a long time. These changes resolve the issue by: 1. Moving the cleanup of the renderer to after the destruction of the old view. This ensures that the usual clean up flow has been kicked off. 2. Flushing the animations when a component is replaced to ensure that the renderer is cleaned up in a timely manner. PR Close #59574
1 parent 612f116 commit a4eb74c

File tree

2 files changed

+13
-6
lines changed

2 files changed

+13
-6
lines changed

packages/animations/browser/src/render/animation_renderer.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ export class AnimationRendererFactory implements RendererFactory2 {
138138
* @param componentId ID of the component that is being replaced.
139139
*/
140140
protected componentReplaced(componentId: string) {
141+
// Flush the engine since the renderer destruction waits for animations to be done.
142+
this.engine.flush();
141143
(this.delegate as {componentReplaced?: (id: string) => void}).componentReplaced?.(componentId);
142144
}
143145
}

packages/core/src/render3/hmr.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
LViewFlags,
3737
NEXT,
3838
PARENT,
39+
RENDERER,
3940
T_HOST,
4041
TVIEW,
4142
} from './interfaces/view';
@@ -156,11 +157,6 @@ function recreateLView(
156157
// Recreate the TView since the template might've changed.
157158
const newTView = getOrCreateComponentTView(newDef);
158159

159-
// Always force the creation of a new renderer to ensure state captured during construction
160-
// stays consistent with the new component definition by clearing any old cached factories.
161-
const rendererFactory = lView[ENVIRONMENT].rendererFactory;
162-
clearRendererCache(rendererFactory, oldDef);
163-
164160
// Create a new LView from the new TView, but reusing the existing TNode and DOM node.
165161
const newLView = createLView(
166162
parentLView,
@@ -170,7 +166,7 @@ function recreateLView(
170166
host,
171167
tNode,
172168
null,
173-
rendererFactory.createRenderer(host, newDef),
169+
null, // The renderer will be created a bit further down once the old one is destroyed.
174170
null,
175171
null,
176172
null,
@@ -183,6 +179,15 @@ function recreateLView(
183179
// Destroy the detached LView.
184180
destroyLView(lView[TVIEW], lView);
185181

182+
// Always force the creation of a new renderer to ensure state captured during construction
183+
// stays consistent with the new component definition by clearing any old ached factories.
184+
const rendererFactory = lView[ENVIRONMENT].rendererFactory;
185+
clearRendererCache(rendererFactory, oldDef);
186+
187+
// Patch a brand-new renderer onto the new view only after the old
188+
// view is destroyed so that the runtime doesn't try to reuse it.
189+
newLView[RENDERER] = rendererFactory.createRenderer(host, newDef);
190+
186191
// Remove the nodes associated with the destroyed LView. This removes the
187192
// descendants, but not the host which we want to stay in place.
188193
removeViewFromDOM(lView[TVIEW], lView);

0 commit comments

Comments
 (0)