Reactive conditional rendering based on Store of objects

Heres an example of what I am trying to do:
import { For, type Component } from "solid-js";
import { createStore } from "solid-js/store";

type Tag = {
id: number;
name: string;
};

const App: Component = () => {
const [allTags] = createStore<Array<Tag>>([
{ id: 0, name: "foo" },
{ id: 1, name: "bar" },
{ id: 2, name: "baz" },
]);

const [appliedTags, setAppliedTags] = createStore<Array<Tag>>([
{ id: 1, name: "bar" },
]);

return (
<>
<ul>
<For each={allTags}>
{(t) =>
appliedTags.some((v) => v.id === t.id) ? (
<li style={"font-weight: bold;"}>{t.name}</li>
) : (
<li>{t.name}</li>
)
}
</For>
</ul>
<button
onClick={() => {
setAppliedTags((cur) => [...cur, { id: 0, name: "foo" }]);
}}
>
Add "foo" tag
</button>
</>
);
};
import { For, type Component } from "solid-js";
import { createStore } from "solid-js/store";

type Tag = {
id: number;
name: string;
};

const App: Component = () => {
const [allTags] = createStore<Array<Tag>>([
{ id: 0, name: "foo" },
{ id: 1, name: "bar" },
{ id: 2, name: "baz" },
]);

const [appliedTags, setAppliedTags] = createStore<Array<Tag>>([
{ id: 1, name: "bar" },
]);

return (
<>
<ul>
<For each={allTags}>
{(t) =>
appliedTags.some((v) => v.id === t.id) ? (
<li style={"font-weight: bold;"}>{t.name}</li>
) : (
<li>{t.name}</li>
)
}
</For>
</ul>
<button
onClick={() => {
setAppliedTags((cur) => [...cur, { id: 0, name: "foo" }]);
}}
>
Add "foo" tag
</button>
</>
);
};
I have two Stores, containing Arrays of the same object. When displaying each object of one Store, I want to format the item based on whether or not the object can be found in the other Store. This is simple enough. However, when I update the second Store, I want the formatting on the displayed items to update reactively. So in this example, when you press the button to add the "foo" tag, the "foo" item printed above should become bold. Thank you for your assistance. (I know it would be easier, in this example, to just have a single Store and an "enabled" field on the Tag, however in my actual application, the above structure is necessary.)
23 Replies
TaQuanMinhLong
Can we let everything live within 1 store?
Бабкины Семечки
Unfortunately not
TaQuanMinhLong
To know which is applied, we just need to have a boolean value
Бабкины Семечки
At least not in a way I can think of. You see, in my actual application, there are many things that take tags, and the tags for each item are stored separately from the main Store of tags.
TaQuanMinhLong
So the tags are locally stored? And not from server? :Worry_Think: Never thought about this, but this is what came to my mind Making the tag in the applied store as a getter instead (a function)
Бабкины Семечки
How do you mean?
TaQuanMinhLong
But this seems too smelly when thinking in the solidjs way :_Worry_Cuoituthien:
Бабкины Семечки
No, tags are stored on the server. This example is dramatically simplified
TaQuanMinhLong
So you need to have some sort of revalidation right? I think there should be an API to query a single tag Then you can cache it as you want But if there's none, I think we need another way :Worry_Think: Like setAppliedTag and the value is a function But the best seems to be just using one global store :Worry_Think: You can write to the store with createComputed to initialize the store Solidjs store is really powerful and not complicated at all
Бабкины Семечки
My application works like this: - I send a request to the server for all tags, and put them in a store. - For a given item, I query the server for the tags applied to that item, and put those tags into another store. - I display all tags, with the applied tags highlighted so that the user can see which tags are applied to the item. When i change the tags applied to an item, I want the display to update reactively
TaQuanMinhLong
type Store = {
tags: Record<TagId, Tag>,
applied: Record<ItemId, Array<TagId>
}
type Store = {
tags: Record<TagId, Tag>,
applied: Record<ItemId, Array<TagId>
}
Then if you need to display, go with a loop through keys and access Set applied is basically modifying the array of tags inside corresponding item
function setAppliedTag() {
setStore("applied", itemId, () => // doing modification)
}
function setAppliedTag() {
setStore("applied", itemId, () => // doing modification)
}
:Worry_WOW: I think the store can have the ability to cover a whole sql table
Бабкины Семечки
I dont think i'm following what you're saying. I refactored the above example and it doesn't work:
const [tags, setTags] = createStore<{
all: Record<TagId, string>;
applied: Record<ItemId, Array<TagId>>;
}>({ all: { 0: "foo", 1: "bar", 2: "baz" }, applied: { 0: [1], 1: [2] } });

return (
<>
<For each={[0, 1]}>
{(itemId) => {
return (
<div>
<div>Item {itemId}:</div>
<ul>
<For each={Object.keys(tags.all)}>
{(key) =>
tags.applied[itemId as ItemId].includes(Number(key)) ? (
<li style={"font-weight: bold;"}>
{tags.all[Number(key)]}
</li>
) : (
<li>{tags.all[Number(key)]}</li>
)
}
</For>
</ul>
<button
onClick={() => {
setTags("applied", itemId as ItemId, (cur) => [...cur, 0]);
}}
>
Add "foo" tag
</button>
</div>
);
}}
</For>
</>
);
const [tags, setTags] = createStore<{
all: Record<TagId, string>;
applied: Record<ItemId, Array<TagId>>;
}>({ all: { 0: "foo", 1: "bar", 2: "baz" }, applied: { 0: [1], 1: [2] } });

return (
<>
<For each={[0, 1]}>
{(itemId) => {
return (
<div>
<div>Item {itemId}:</div>
<ul>
<For each={Object.keys(tags.all)}>
{(key) =>
tags.applied[itemId as ItemId].includes(Number(key)) ? (
<li style={"font-weight: bold;"}>
{tags.all[Number(key)]}
</li>
) : (
<li>{tags.all[Number(key)]}</li>
)
}
</For>
</ul>
<button
onClick={() => {
setTags("applied", itemId as ItemId, (cur) => [...cur, 0]);
}}
>
Add "foo" tag
</button>
</div>
);
}}
</For>
</>
);
Is this what you meant?
TaQuanMinhLong
Which part of it doesn't work? Solid also has their Conditional Rendering components like <Show> so you don't need to use ternary operator For appending value to array, see store guide https://docs.solidjs.com/concepts/stores
Бабкины Семечки
The desired behavior doesn't happen. Adding the new applied tag does not update the formatting of of the displayed list
TaQuanMinhLong
Can you try logging the store as you appended? If the store's values are updated, then there was something wrong with the rendering Not the store logic
bigmistqke
bigmistqke2d ago
it has something to do with the ternary
bigmistqke
bigmistqke2d ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
bigmistqke
bigmistqke2d ago
a i found the reason why it doesn't work: you are returning the ternary directly inside <For/>
bigmistqke
bigmistqke2d ago
returning directly (your snippet)
No description
bigmistqke
bigmistqke2d ago
wrapping it in a fragment <></>
No description
bigmistqke
bigmistqke2d ago
it's not so clear when you return it directly, but when you write it a bit differently it becomes obvious why your original snippet is not reactive: it accesses the store inside the component-body
No description
bigmistqke
bigmistqke2d ago
another good reason why to use control flow components in solidjs, was a tricky to spot bug
Бабкины Семечки
Thank you for the solution and the detailed explanation 👍
Want results from more Discord servers?
Add your server