-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
In qwik, if you have 2x simple components and have them added to Builder as blocks:
- Tab (provides a context for what is selected)
- TabPane (reads context and does a hide/show)
You'll see that reactivity breaks when EnableEditor comes alive (you're editing in Builder or previewing). This is how the EnableEditor component is merging the new content:
You can fix it by copying how Qwik REPL does a deepUpdate (tested and verified it works): https://github.com/QwikDev/qwik/blob/main/packages/docs/src/repl/ui/repl-output-update.ts
Then it's as simple as:
import { unwrapStore } from '@builder.io/qwik';
const deepUpdate = (prev: any, next: any) => {
for (const key in next) {
if (prev[key] && typeof next[key] === 'object' && typeof prev[key] === 'object') {
deepUpdate(prev[key], next[key]);
} else {
if (unwrapStore(prev[key]) !== next[key]) {
prev[key] = next[key];
}
}
}
if (Array.isArray(prev)) {
if (prev.length !== next.length) {
prev.length = next.length;
}
} else {
for (const key in prev) {
if (!(key in next)) {
delete prev[key];
}
}
}
};
mergeNewContent(newContent: BuilderContent, editType?: EditType) {
deepUpdate(props.builderContextSignal.content, newContent);
}And you have reactivity again in both default/editing mode.
Example of tabs with a context for which one is visible:
import { component$, createContextId, Signal, Slot, useContext, useContextProvider, useSignal } from '@builder.io/qwik';
const tabContextId = createContextId<Signal<string>>('active-tab-slug');
interface TabHeader {
slug: string;
text: string;
}
export const Tabs = component$(({ tabs }: { tabs: TabHeader[] }) => {
const activeTabSlug = useSignal('one');
useContextProvider(tabContextId, activeTabSlug);
return (
<div class="tabs">
<nav>
{tabs.map(tab => <button key={tab.slug} onClick$={() => activeTabSlug.value = tab.slug}>{tab.text}</button>)}
</nav>
<Slot />
</div>
)
});
export const TabPane = component$(({ slug }: { slug: string }) => {
const active = useContext(tabContextId);
return (
<div class={{ visible: slug === active.value}}>
<Slot />
</div>
)
});And then via Builder CMS you can have blocks that provide a structure like this:
<Tabs tabs={[{ slug: "one", text: "One" }, { slug: "two", text: "Two" }]}>
<TabPane slug='one'>
<Text text="one content" />
</TabPane>
<TabPane slug='two'>
<Text text="two content" />
</TabPane>
</Tabs>You won't be able to click between those tabs within Builder when editing. I think it works once but then breaks.