Skip to content

Commit 4672d60

Browse files
authored
fix: keep state in parent to avoid infinite rendering (#1825)
Signed-off-by: Simon Rey <[email protected]>
1 parent 163ecda commit 4672d60

File tree

2 files changed

+38
-52
lines changed

2 files changed

+38
-52
lines changed

packages/frontend/src/lib/examples/Examples.svelte

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,51 +5,67 @@ import { NavPage } from '@podman-desktop/ui-svelte';
55
import { bootcClient } from '/@/api/client';
66
import ExamplesCard from './ExamplesCard.svelte';
77
import { SvelteMap } from 'svelte/reactivity';
8+
import { imageInfo } from '/@/stores/imageInfo';
89
9-
let groups: Map<Category, Example[]> = new Map();
10+
let baseExamples = $state<Example[]>([]);
11+
let baseCategories = $state<Category[]>([]);
1012
1113
const UNCLASSIFIED: Category = {
1214
id: 'unclassified',
1315
name: 'Unclassified',
1416
};
1517
1618
onMount(async () => {
17-
// onmount get the examples
18-
let examples = await bootcClient.getExamples();
19+
const data = await bootcClient.getExamples();
20+
baseExamples = data.examples;
21+
baseCategories = data.categories;
22+
});
23+
24+
const examplesWithState = $derived.by(() => {
25+
const pulledImages = new Set($imageInfo?.map(image => image.RepoTags?.[0] ?? '') ?? []);
26+
return baseExamples.map(example => {
27+
const exampleWithState: Example = {
28+
...example,
29+
state: pulledImages.has(`${example.image}:${example.tag}`) ? 'pulled' : 'unpulled',
30+
};
31+
return exampleWithState;
32+
});
33+
});
1934
20-
const categoryDict = Object.fromEntries(examples.categories.map((category: Category) => [category.id, category]));
35+
const groups = $derived.by(() => {
36+
const newGroups = new SvelteMap<Category, Example[]>();
37+
const categoryDict = Object.fromEntries(baseCategories.map(c => [c.id, c]));
2138
22-
const output: SvelteMap<Category, Example[]> = new SvelteMap();
39+
for (const example of examplesWithState) {
40+
const addExampleToCategory = (key: Category): void => {
41+
const list = newGroups.get(key);
42+
if (list) {
43+
list.push(example);
44+
} else {
45+
newGroups.set(key, [example]);
46+
}
47+
};
2348
24-
for (const example of examples.examples) {
2549
if (example.categories.length === 0) {
26-
output.set(UNCLASSIFIED, [...(output.get(UNCLASSIFIED) ?? []), example]);
50+
addExampleToCategory(UNCLASSIFIED);
2751
continue;
2852
}
2953
30-
// iterate over all categories
3154
for (const categoryId of example.categories) {
32-
let key: Category;
33-
if (categoryId in categoryDict) {
34-
key = categoryDict[categoryId];
35-
} else {
36-
key = UNCLASSIFIED;
37-
}
38-
39-
output.set(key, [...(output.get(key) ?? []), example]);
55+
const key = categoryDict[categoryId] ?? UNCLASSIFIED;
56+
addExampleToCategory(key);
4057
}
4158
}
42-
43-
groups = output;
59+
return newGroups;
4460
});
4561
</script>
4662

4763
<NavPage title="Examples" searchEnabled={false}>
4864
<div slot="content" class="flex flex-col min-w-full min-h-full">
4965
<div class="min-w-full min-h-full flex-1">
5066
<div class="px-5 space-y-5">
51-
{#each groups.entries() as [category, examples](category.id)}
52-
<ExamplesCard category={category} examples={examples} />
67+
{#each groups.entries() as [category, examples] (category.id)}
68+
<ExamplesCard {category} {examples} />
5369
{/each}
5470
</div>
5571
</div>

packages/frontend/src/lib/examples/ExamplesCard.svelte

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,19 @@
22
import Card from '/@/lib/Card.svelte';
33
import ExampleCard from './ExampleCard.svelte';
44
import type { Example, Category } from '/@shared/src/models/examples';
5-
import { imageInfo } from '../../stores/imageInfo';
65
76
interface Props {
87
category: Category;
98
examples: Example[];
109
}
11-
let { category, examples = $bindable() }: Props = $props();
12-
13-
// Function to update examples based on available images
14-
function updateExamplesWithPulledImages(): void {
15-
if ($imageInfo) {
16-
// Set each state to 'unpulled' by default before updating, as this prevents 'flickering'
17-
// and unsure states when images are being updated
18-
for (const example of examples) {
19-
example.state = 'unpulled';
20-
}
21-
22-
for (const image of $imageInfo) {
23-
// Only do it if there is a RepoTags
24-
const [imageRepo, imageTag] = image.RepoTags?.[0]?.split(':') ?? [];
25-
// Find by image name and tag if it's in the list of examples
26-
const example = examples.find(example => example.image === imageRepo && example.tag === imageTag);
27-
if (example) {
28-
example.state = 'pulled';
29-
}
30-
}
31-
32-
// Update examples to trigger a re-render
33-
examples = [...examples];
34-
}
35-
}
36-
37-
// Reactive statement to call the function each time bootcAvailableImages updates
38-
$effect(() => {
39-
updateExamplesWithPulledImages();
40-
});
10+
let { category, examples }: Props = $props();
4111
</script>
4212

4313
<Card title={category.name}>
4414
<div class="w-full">
4515
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mt-4">
4616
{#each examples as example (example.id)}
47-
<ExampleCard example={example} />
17+
<ExampleCard {example} />
4818
{/each}
4919
</div>
5020
</div>

0 commit comments

Comments
 (0)