Skip to content

Commit

Permalink
Merge pull request #7155 from QwikDev/v2-fix-store-deserialization
Browse files Browse the repository at this point in the history
fix: prevent multiple store deserialization
  • Loading branch information
shairez authored Dec 13, 2024
2 parents f75361c + 5352f6f commit 9481991
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/chilled-spoons-wonder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@qwik.dev/core': patch
---

fix: prevent multiple store deserialization
5 changes: 4 additions & 1 deletion packages/qwik/src/core/shared/shared-serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,10 @@ const inflate = (container: DeserializeContainer, target: any, typeId: TypeIds,
if (valType === TypeIds.RootRef || valType >= TypeIds.Error) {
Object.defineProperty(target, key, {
get() {
return deserializeData(container, valType, valData);
const value = deserializeData(container, valType, valData);
// after first deserialize, we can replace the Object.defineProperty with the value
target[key] = value;
return value;
},
set(value: unknown) {
Object.defineProperty(target, key, {
Expand Down
65 changes: 65 additions & 0 deletions packages/qwik/src/core/tests/use-store.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
Fragment as Component,
Fragment as InlineComponent,
component$,
Fragment,
Fragment as Signal,
Expand All @@ -8,6 +9,7 @@ import {
useStore,
useTask$,
useVisibleTask$,
type PropsOf,
} from '@qwik.dev/core';
import { domRender, ssrRenderToDom, trigger } from '@qwik.dev/core/testing';
import { describe, expect, it, vi } from 'vitest';
Expand Down Expand Up @@ -285,6 +287,69 @@ describe.each([
</Component>
);
});

it('should rerender inner component with store as a prop', async () => {
interface InnerButtonProps {
text: string;
isActive: boolean;
onClick$: PropsOf<'button'>['onClick$'];
}
const InnerButton = component$((props: InnerButtonProps) => {
return (
<button
key={props.text}
class={{ 'active-tab': props.isActive, 'repl-tab-button': true }}
onClick$={props.onClick$}
>
{props.text}
</button>
);
});

const InnerButtonWrapper = component$((props: { data: any }) => {
return (
<InnerButton
text="Options"
isActive={props.data.selectedOutputDetail === 'options'}
onClick$={() => {
props.data.selectedOutputDetail = 'options';
}}
/>
);
});

const Parent = component$(() => {
const store = useStore({
selectedOutputDetail: 'console',
});

return <InnerButtonWrapper data={store} />;
});

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

expect(vNode).toMatchVDOM(
<Component ssr-required>
<InlineComponent>
<InlineComponent>
<button class="repl-tab-button">Options</button>
</InlineComponent>
</InlineComponent>
</Component>
);

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

expect(vNode).toMatchVDOM(
<Component ssr-required>
<InlineComponent>
<InlineComponent>
<button class="active-tab repl-tab-button">Options</button>
</InlineComponent>
</InlineComponent>
</Component>
);
});
});

describe('SerializationConstant at the start', () => {
Expand Down

0 comments on commit 9481991

Please sign in to comment.