Skip to content

Commit

Permalink
mark event listeners inside loops as var props
Browse files Browse the repository at this point in the history
  • Loading branch information
Varixo committed Nov 10, 2024
1 parent 7bd2fff commit 02ccb50
Show file tree
Hide file tree
Showing 6 changed files with 704 additions and 57 deletions.
144 changes: 144 additions & 0 deletions packages/qwik/src/core/tests/use-lexical-scope.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { domRender, ssrRenderToDom, trigger } from '@qwik.dev/core/testing';
import { describe, expect, it } from 'vitest';
import {
component$,
useStore,
useSignal,
Fragment as Component,
Fragment as Signal,
} from '@qwik.dev/core';

const debug = false; //true;
Error.stackTraceLimit = 100;

describe.each([
{ render: ssrRenderToDom }, //
{ render: domRender }, //
])('$render.name: useLexicalScope', ({ render }) => {
it('should update const prop event value', async () => {
type Cart = string[];

const Parent = component$(() => {
const cart = useStore<Cart>([]);
const results = useSignal(['foo']);

return (
<div>
<button id="first" onClick$={() => (results.value = ['item'])}></button>

{results.value.map((item) => (
<button
id="second"
onClick$={() => {
cart.push(item);
}}
>
{item}
</button>
))}
<ul>
{cart.map((item) => (
<li>
<span>{item}</span>
</li>
))}
</ul>
</div>
);
});

const { vNode, document } = await render(<Parent />, { debug });

expect(vNode).toMatchVDOM(
<Component>
<div>
<button id="first"></button>
<button id="second">foo</button>
<ul></ul>
</div>
</Component>
);

await trigger(document.body, 'button#first', 'click');

expect(vNode).toMatchVDOM(
<Component>
<div>
<button id="first"></button>
<button id="second">item</button>
<ul></ul>
</div>
</Component>
);

await trigger(document.body, 'button#second', 'click');

expect(vNode).toMatchVDOM(
<Component>
<div>
<button id="first"></button>
<button id="second">item</button>
<ul>
<li>
<span>item</span>
</li>
</ul>
</div>
</Component>
);
});

describe('regression', () => {
it('#5662 - should update value in the list', async () => {
/**
* ROOT CAUSE ANALYSIS: This is a bug in Optimizer. The optimizer incorrectly marks the
* `onClick` listener as 'const'/'immutable'. Because it is const, the QRL associated with the
* click handler always points to the original object, and it is not updated.
*/
const Cmp = component$(() => {
const store = useStore<{ users: { name: string }[] }>({ users: [{ name: 'Giorgio' }] });

return (
<div>
{store.users.map((user, key) => (
<span
key={key}
onClick$={() => {
store.users = store.users.map(({ name }: { name: string }) => ({
name: name === user.name ? name + '!' : name,
}));
}}
>
{user.name}
</span>
))}
</div>
);
});
const { vNode, container } = await render(<Cmp />, { debug });
expect(vNode).toMatchVDOM(
<Component>
<div>
<span key="0">
<Signal>{'Giorgio'}</Signal>
</span>
</div>
</Component>
);
await trigger(container.element, 'span', 'click');
await trigger(container.element, 'span', 'click');
await trigger(container.element, 'span', 'click');
await trigger(container.element, 'span', 'click');
await trigger(container.element, 'span', 'click');
expect(vNode).toMatchVDOM(
<Component>
<div>
<span key="0">
<Signal>{'Giorgio!!!!!'}</Signal>
</span>
</div>
</Component>
);
});
});
});
52 changes: 0 additions & 52 deletions packages/qwik/src/core/tests/use-store.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -882,58 +882,6 @@ describe.each([
vi.useRealTimers();
});

it.skip('#5662 - should update value in the list', async () => {
/**
* ROOT CAUSE ANALYSIS: This is a bug in Optimizer. The optimizer incorrectly marks the
* `onClick` listener as 'const'/'immutable'. Because it is const, the QRL associated with the
* click handler always points to the original object, and it is not updated.
*/
const Cmp = component$(() => {
const store = useStore<{ users: { name: string }[] }>({ users: [{ name: 'Giorgio' }] });

return (
<div>
{store.users.map((user, key) => (
<span
key={key}
onClick$={() => {
store.users = store.users.map(({ name }: { name: string }) => ({
name: name === user.name ? name + '!' : name,
}));
}}
>
{user.name}
</span>
))}
</div>
);
});
const { vNode, container } = await render(<Cmp />, { debug });
expect(vNode).toMatchVDOM(
<Component>
<div>
<span key="0">
<Signal>{'Giorgio'}</Signal>
</span>
</div>
</Component>
);
await trigger(container.element, 'span', 'click');
await trigger(container.element, 'span', 'click');
await trigger(container.element, 'span', 'click');
await trigger(container.element, 'span', 'click');
await trigger(container.element, 'span', 'click');
expect(vNode).toMatchVDOM(
<Component>
<div>
<span key="0">
<Signal>{'Giorgio!!!!!'}</Signal>
</span>
</div>
</Component>
);
});

it('#5017 - should update child nodes for direct array', async () => {
const Child = component$<{ columns: string }>(({ columns }) => {
return <div>Child: {columns}</div>;
Expand Down
Loading

0 comments on commit 02ccb50

Please sign in to comment.