Skip to content

Commit d1b96d3

Browse files
authored
Merge pull request #8164 from sashkashishka/bugfix/cleantree-stops-on-first-slot-in-dom-render-fn-cleanup
fix: execute cleanup cb for all component tree while calling dispose.cleanup method returned by render fn
2 parents b3a5f7e + 36d4b7e commit d1b96d3

File tree

10 files changed

+103
-18
lines changed

10 files changed

+103
-18
lines changed

.changeset/bumpy-cycles-carry.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'eslint-plugin-qwik': patch
3+
'create-qwik': patch
4+
'@builder.io/qwik-react': patch
5+
'@builder.io/qwik-city': patch
6+
'@builder.io/qwik': patch
7+
---
8+
9+
execute cleanup cb for all component tree while calling dispose.cleanup method returned by render fn

packages/qwik/src/core/render/dom/render.public.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export const injectQContainer = (containerEl: Element) => {
124124

125125
function cleanupContainer(renderCtx: RenderContext, container: Element) {
126126
const subsManager = renderCtx.$static$.$containerState$.$subsManager$;
127-
cleanupTree(container, renderCtx.$static$, subsManager, true);
127+
cleanupTree(container, renderCtx.$static$, subsManager, true, true);
128128

129129
removeContainerState(container);
130130

packages/qwik/src/core/render/dom/render.unit.tsx

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -778,14 +778,37 @@ test('should clean up subscriptions after calling the returned cleanup function'
778778
const fixture = new ElementFixture();
779779

780780
const spies = {
781-
cleanupSpy: false,
781+
cleanup: false,
782782
};
783783

784784
const { cleanup } = await render(fixture.host, <CleanupComponent spies={spies} />);
785785

786786
cleanup();
787787

788-
assert.equal(spies.cleanupSpy, true);
788+
assert.equal(spies.cleanup, true);
789+
});
790+
791+
test('should clean up nested subscriptions after calling the returned cleanup function', async () => {
792+
const fixture = new ElementFixture();
793+
794+
const spies = {
795+
parentCleanup: false,
796+
cleanup: false,
797+
slottedCleanup: false,
798+
};
799+
800+
const { cleanup } = await render(
801+
fixture.host,
802+
<ParentCleanupComponent spies={spies}>
803+
<SlottedCleanupComponent spies={spies} />
804+
</ParentCleanupComponent>
805+
);
806+
807+
cleanup();
808+
809+
assert.equal(spies.parentCleanup, true);
810+
assert.equal(spies.cleanup, true);
811+
assert.equal(spies.slottedCleanup, true);
789812
});
790813

791814
async function expectRendered(fixture: ElementFixture, expected: string) {
@@ -1045,16 +1068,56 @@ export const Hooks = component$(() => {
10451068

10461069
//////////////////////////////////////////////////////////////////////////////////////////
10471070

1048-
export const CleanupComponent = component$((props: { spies: { cleanupSpy: boolean } }) => {
1071+
interface CleanupProps {
1072+
spies: {
1073+
parentCleanup?: boolean;
1074+
cleanup?: boolean;
1075+
slottedCleanup?: boolean;
1076+
};
1077+
}
1078+
1079+
export const ParentCleanupComponent = component$((props: CleanupProps) => {
10491080
useTask$(({ cleanup }) => {
10501081
cleanup(() => {
1051-
props.spies.cleanupSpy = true;
1082+
props.spies.parentCleanup = true;
1083+
});
1084+
});
1085+
1086+
return (
1087+
<div>
1088+
<div id="parent-cleanup">true</div>
1089+
<CleanupComponent spies={props.spies}>
1090+
<Slot />
1091+
</CleanupComponent>
1092+
</div>
1093+
);
1094+
});
1095+
1096+
export const CleanupComponent = component$((props: CleanupProps) => {
1097+
useTask$(({ cleanup }) => {
1098+
cleanup(() => {
1099+
props.spies.cleanup = true;
10521100
});
10531101
});
10541102

10551103
return (
10561104
<div>
10571105
<div id="cleanup">true</div>
1106+
<Slot />
1107+
</div>
1108+
);
1109+
});
1110+
1111+
export const SlottedCleanupComponent = component$((props: CleanupProps) => {
1112+
useTask$(({ cleanup }) => {
1113+
cleanup(() => {
1114+
props.spies.slottedCleanup = true;
1115+
});
1116+
});
1117+
1118+
return (
1119+
<div>
1120+
<div id="slotted-cleanup">true</div>
10581121
</div>
10591122
);
10601123
});

packages/qwik/src/core/render/dom/visitor.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,11 +1135,12 @@ export const cleanupTree = (
11351135
elm: Node | VirtualElement,
11361136
staticCtx: RenderStaticContext,
11371137
subsManager: SubscriptionManager,
1138-
stopSlots: boolean
1138+
stopSlots: boolean,
1139+
dispose = false
11391140
) => {
11401141
subsManager.$clearSub$(elm);
11411142
if (isQwikElement(elm)) {
1142-
if (stopSlots && elm.hasAttribute(QSlotS)) {
1143+
if (!dispose && stopSlots && elm.hasAttribute(QSlotS)) {
11431144
staticCtx.$rmSlots$.push(elm);
11441145
return;
11451146
}
@@ -1150,7 +1151,7 @@ export const cleanupTree = (
11501151
const end = isVirtualElement(elm) ? elm.close : null;
11511152
let node: Node | null | VirtualElement = elm.firstChild;
11521153
while ((node = processVirtualNodes(node))) {
1153-
cleanupTree(node!, staticCtx, subsManager, true);
1154+
cleanupTree(node!, staticCtx, subsManager, true, dispose);
11541155
node = node.nextSibling;
11551156
if (node === end) {
11561157
break;

starters/apps/starter-partytown-test/src/root.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ export default () => {
99
<head>
1010
<meta charset="utf-8" />
1111
<title>Qwik + Partytown Blank App</title>
12-
<script dangerouslySetInnerHTML={partytownSnippet({ debug: true })} />
12+
<script
13+
dangerouslySetInnerHTML={partytownSnippet({
14+
fallbackTimeout: 1000,
15+
debug: true,
16+
})}
17+
/>
1318
</head>
1419
<body>
1520
<App />

starters/e2e/e2e.attributes.spec.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,13 @@ test.describe("attributes", () => {
302302
test.describe("client rerender", () => {
303303
test.beforeEach(async ({ page }) => {
304304
const toggleRender = page.locator("#force-rerender");
305+
const renderCount = page.locator("#renderCount");
305306
const v = Number(await toggleRender.getAttribute("data-v"));
307+
308+
expect(v).toBe(0);
309+
await expect(renderCount).toHaveText(`Render ${v}`);
306310
await toggleRender.click();
307-
await expect(page.locator("#renderCount")).toHaveText(`Render ${v}`);
308-
// Without this the tests still fail?
309-
await page.waitForTimeout(50);
311+
await expect(renderCount).toHaveText(`Render ${v + 1}`);
310312
});
311313
tests();
312314
});

starters/e2e/e2e.context.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,6 @@ test.describe("context", () => {
187187
test.beforeEach(async ({ page }) => {
188188
const rerender = page.locator("#btn-rerender");
189189
await rerender.click();
190-
await page.waitForTimeout(100);
191190
});
192191
tests();
193192
});

starters/e2e/e2e.style.spec.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@ test.describe("styles", () => {
1717
test.describe("client side", () => {
1818
test.beforeEach(async ({ page }) => {
1919
const reload = page.locator("#reload");
20+
const renderCount = page.locator("#renderCount");
2021
const v = Number(await reload.getAttribute("v"));
22+
23+
expect(v).toBe(0);
24+
await expect(renderCount).toHaveText(`Render ${v}`);
2125
await reload.click();
22-
await expect(page.locator("#renderCount")).toHaveText(`Render ${v}`);
26+
await expect(renderCount).toHaveText(`Render ${v + 1}`);
2327
});
2428
runTests();
2529
});

starters/e2e/qwikcity/nav.spec.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,6 @@ test.describe("actions", () => {
5959

6060
const link = page.locator("#hash-1");
6161
await link.click();
62-
// Without this, sometimes the URL is #hash-1
63-
await page.waitForTimeout(100);
6462

6563
await expect(page).toHaveURL(
6664
"/qwikcity-test/scroll-restoration/hash/#hash-2",

starters/playwright.config.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ expect.extend({
1919

2020
const config: PlaywrightTestConfig = {
2121
use: {
22+
launchOptions: {
23+
slowMo: 100,
24+
},
2225
viewport: {
2326
width: 520,
2427
height: 600,
@@ -27,8 +30,9 @@ const config: PlaywrightTestConfig = {
2730
/* Fail the build on CI if you accidentally left test.only in the source code. */
2831
forbidOnly: !!process.env.CI,
2932
testIgnore: /.*example.spec.tsx?$/,
30-
retries: inGithubCI ? 0 : 1,
31-
expect: { timeout: inGithubCI ? 120000 : 10000 },
33+
retries: 0,
34+
// retries: inGithubCI ? 0 : 1,
35+
expect: { timeout: inGithubCI ? 120000 : 3000 },
3236
webServer: {
3337
command:
3438
"pnpm node --require ./scripts/runBefore.ts starters/dev-server.ts 3301",

0 commit comments

Comments
 (0)