Setting infinitely deep store?

I'm building a website builder, and my model looks like this:
type Element = {
elements?: Element[]
content?: Content[]
}
type Element = {
elements?: Element[]
content?: Content[]
}
Then I have a store like this:
const [pageStore, setPageStore] = createStore<Element>()
const [pageStore, setPageStore] = createStore<Element>()
How would I go about setting the content in the nth depth, without knowing how deep it is?
setPageStore('elements', 4, 'elements', 2, 'elements', 8, 'content', 0, 'html', '<h1>Hello World!</h1>')
setPageStore('elements', 4, 'elements', 2, 'elements', 8, 'content', 0, 'html', '<h1>Hello World!</h1>')
Can I do some sort of loop inside the setPageStore?
18 Replies
Brendonovich
Brendonovich•2w ago
You can use produce and do a loop in there, but you may be better off using a mutable store with createMutable. createStore tends to be pretty finicky when building recursive components, you just have to be careful with createMutable to batch updates yourself as necessary
bigmistqke
bigmistqke•2w ago
cool, relatively unknown feature of solid's store is the following:
const [store, setStore] = createStore({
userCount: 3,
users: [ ... ],
})

const [users, setUsers] = createStore(store.users)

setUsers((currentUsers) => [
...currentUsers,
{
id: 3,
username: "michael584",
location: "Nigeria",
loggedIn: false,
},
])
const [store, setStore] = createStore({
userCount: 3,
users: [ ... ],
})

const [users, setUsers] = createStore(store.users)

setUsers((currentUsers) => [
...currentUsers,
{
id: 3,
username: "michael584",
location: "Nigeria",
loggedIn: false,
},
])
can be very handy for this type of nested, dynamic trees it is mentioned here in the docs
andahendriksen
andahendriksenOP•2w ago
Thanks for the produce suggestion, I've never heard about that! But I'm unable to find an example of using it with a loop. Do you have an example?
bigmistqke
bigmistqke•2w ago
You could do something like:
setStore(produce(store => {
store.users.forEach(user => {
// mutate user here
})
}))
setStore(produce(store => {
store.users.forEach(user => {
// mutate user here
})
}))
inside the produce-callback you can mutate the store as you wish
andahendriksen
andahendriksenOP•2w ago
Thanks for the example! Will play around with it a bit and see if I can get it to work for my case!
bigmistqke
bigmistqke•2w ago
you are very welcome!
andahendriksen
andahendriksenOP•2w ago
I got it to work! But I don't know if there's a better way to do it? I've come up with this:
function findElementToUpdate(
element: Element,
element_indexes: number[],
index_level: number,
element_to_update?: Element,
content_to_update?: Content,
content_index?: number
) {
const has_next_index = element_indexes.length !== index_level

if (has_next_index) {
const next_index = element_indexes[index_level]

return findElementToUpdate(
element.elements[next_index],
element_indexes,
index_level + 1,
element_to_update,
content_to_update)
}

if (content_to_update) {
element.content[content_index] = {
...element.content[content_index],
...content_to_update
}
}

if (element_to_update) {
element = {
...element,
...element_to_update
}
}
}

export function setPageStoreElement(
element_indexes: number[],
element_to_update?: Element,
content_to_update?: Content,
content_index?: number
) {
setPageStore(produce((pageStore) => {
findElementToUpdate(
pageStore.page.elements[element_indexes[0]],
element_indexes,
1,
element_to_update,
content_to_update,
content_index)
}))
}
function findElementToUpdate(
element: Element,
element_indexes: number[],
index_level: number,
element_to_update?: Element,
content_to_update?: Content,
content_index?: number
) {
const has_next_index = element_indexes.length !== index_level

if (has_next_index) {
const next_index = element_indexes[index_level]

return findElementToUpdate(
element.elements[next_index],
element_indexes,
index_level + 1,
element_to_update,
content_to_update)
}

if (content_to_update) {
element.content[content_index] = {
...element.content[content_index],
...content_to_update
}
}

if (element_to_update) {
element = {
...element,
...element_to_update
}
}
}

export function setPageStoreElement(
element_indexes: number[],
element_to_update?: Element,
content_to_update?: Content,
content_index?: number
) {
setPageStore(produce((pageStore) => {
findElementToUpdate(
pageStore.page.elements[element_indexes[0]],
element_indexes,
1,
element_to_update,
content_to_update,
content_index)
}))
}
And then to update the content in child elements:
const content_to_update = { html: '<h1>Hello World</h1>' }
setPageStoreElement([4, 2, 8], null, content_to_update, 0)
const content_to_update = { html: '<h1>Hello World</h1>' }
setPageStoreElement([4, 2, 8], null, content_to_update, 0)
bigmistqke
bigmistqke•2w ago
(could u add syntax highlighting to the snippet? add tsx after the triple backticks) the first backticks thank u!
andahendriksen
andahendriksenOP•2w ago
Hahaha you saw that! 😄 Thx
bigmistqke
bigmistqke•2w ago
what i am not completely getting from your code is why not do setStore('page', 'elements', element_indexes[0], ...) ? pageStore.page.elements[element_indexes[0]] this line gives me the impression that you do know the path, but mb i m misunderstanding the code
andahendriksen
andahendriksenOP•2w ago
element_indexes is to tell how deep in the element tree I'm going. It can be:
[4, 2, 8]
[4, 2, 8]
or
[1, 4, 9, 3, 2, 5, 2, 1]
[1, 4, 9, 3, 2, 5, 2, 1]
But I don't know which one it will be. So fo each of the indexes in the element_indexes I'll go deeper. Does that make sense?
bigmistqke
bigmistqke•2w ago
o i see I think there might be a cleaner way of expressing this, something like:
setStore(produce(store => {
let currentElement
while(element_indexes.length > 0){
currentElement = (currentElement ?? store.page).elements[element_indexes.shift()];
}
// mutate currentElement
}))
setStore(produce(store => {
let currentElement
while(element_indexes.length > 0){
currentElement = (currentElement ?? store.page).elements[element_indexes.shift()];
}
// mutate currentElement
}))
but i think the main idea is good: just walk that tree
andahendriksen
andahendriksenOP•2w ago
lol that is so much cleaner! Knew there would be a simpler way haha thank you so much for your help, really appreciate it! 😎
bigmistqke
bigmistqke•2w ago
you are very welcome! often when doing recursion you can get away with doing loops too especially w this type of nested tree stuff it can get spaghetti very fast!
andahendriksen
andahendriksenOP•2w ago
Yes, especially for me as a much more visual frontend/ux designer. I'm not good at "seeing" the logical way to code. Guess I'll have to hire an Italian chef in the future to clean up all the spaghetti code I've made haha
bigmistqke
bigmistqke•2w ago
also don't know if you already saw it, but since you mentioned you are building a website builder: https://discord.com/channels/722131463138705510/1201177657480577195
andahendriksen
andahendriksenOP•2w ago
Cool! Thanks for the link to nitro! I'm taking a bit of a different approach though, building a stupid webbuilder for people that don't know what columns or paddings are haha
bigmistqke
bigmistqke•2w ago
the more solid web builders the merrier if you have make some progress at some point, definitely share in https://discord.com/channels/722131463138705510/734440921776783391 would love to follow up
Want results from more Discord servers?
Add your server