S
SolidJS•3mo ago
gsoutz

How to sync two different signals inside a store maybe

I have an array and an index to the array defined separately as two signals, here I've demonstrated:
import { batch, createComputed, createMemo, createSignal, on, untrack } from "solid-js";
import { createStore } from "solid-js/store";
import { render } from "solid-js/web";


function App() {

const [my_array, set_my_array] = createSignal([3])

const array = createMemo(() => my_array())

const [p_store, set_p_store] = createStore({
get array() {
return array()
},
cursor: 0
})


const fail_computation = createMemo(() => p_store.array[p_store.cursor].array_access)

batch(() => {
set_my_array([1, 2, 3, 4])
set_p_store('cursor', 3)
})

const on_reset_array = () => {
set_my_array([])
}


return (<><button onClick={on_reset_array}></button> </>)
}


render(() => <App />, document.getElementById("app")!);
import { batch, createComputed, createMemo, createSignal, on, untrack } from "solid-js";
import { createStore } from "solid-js/store";
import { render } from "solid-js/web";


function App() {

const [my_array, set_my_array] = createSignal([3])

const array = createMemo(() => my_array())

const [p_store, set_p_store] = createStore({
get array() {
return array()
},
cursor: 0
})


const fail_computation = createMemo(() => p_store.array[p_store.cursor].array_access)

batch(() => {
set_my_array([1, 2, 3, 4])
set_p_store('cursor', 3)
})

const on_reset_array = () => {
set_my_array([])
}


return (<><button onClick={on_reset_array}></button> </>)
}


render(() => <App />, document.getElementById("app")!);
. Now when I update the array for example reset it, the cursor still shows the old location which causes an out of bounds error. How do I structure my code to prevent this kind of issue, and still be able to have a computation to access inside the array reactively. https://playground.solidjs.com/anonymous/f3de692a-5ace-4a08-a51c-dc8541ec0b85
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
26 Replies
gsoutz
gsoutzOP•3mo ago
I wouldn't prefer to use a batch to whenever the array changes just reset the cursor along with it. Because the array is not a simple signal it's computed from other different signals, for example I filter an unrelated stuff to reconstruct this array.
bigmistqke
bigmistqke•3mo ago
maybe like this 👉
const fail_computation = createMemo(() => p_store.array[p_store.cursor]?.array_access)
const fail_computation = createMemo(() => p_store.array[p_store.cursor]?.array_access)
? https://playground.solidjs.com/anonymous/df57c303-5ec5-4fcd-b6ca-9143f55a1765
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
gsoutz
gsoutzOP•3mo ago
I just wonder if there is a pattern to keep two signals in a consistent sync state based on some constrained relationship with each other. because you see i never intended the cursor to point to dangled reference in the array. whenever the array changes I want to reset the cursor back to zero that way the fail_computation would never fail or be undefined, because I know array always has at least one element and when array changes and cursor resets back to zero, fail_computation still always references something. I just can't do that in a batch, because array is derived from another computation that is hard to control when it is set.
bigmistqke
bigmistqke•3mo ago
There will be a new primitive in solid 2.0: createProjection
Solid.js v2
Rough WIP docs for Solid 2.0.
gsoutz
gsoutzOP•3mo ago
I am confused by what state._active is doing, is that some internal flag to indicate something changing.
bigmistqke
bigmistqke•3mo ago
That primitive should help with derived values inside stores. I think ._active is like your .cursor
gsoutz
gsoutzOP•3mo ago
And If I can simultaneously set more than one property inside that projection then I can both calculate the derived array and set the cursor to 0 at the same time which should be a solution to my problem. ._active is never used so I don't understand it's meaning.
bigmistqke
bigmistqke•3mo ago
The example could be a bit more intuitive, sure. But I hope you can zoom out and see the full picture and how it relates to your problem.
bigmistqke
bigmistqke•3mo ago
for now, you could do something like this where you try/catch inside the memo and set the cursor to 0 if it fails. or you could do something like this where you compare the cursor with the array-length inside the memo and reset the cursor to 0 if the cursor exceeds the length.
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
bigmistqke
bigmistqke•3mo ago
it's generally not adviced to set signals inside memos, but rules are there to be bended note that I add a default item to the array in the array-memo to assure there will always be 1 item in the array.
bigmistqke
bigmistqke•3mo ago
you could also have a conditional in a getter of cursor like this. it's a pity that you can not do const [store, setStore] = createSignal({ get signal() { return signal() }, set signal(value){ setSignal(value) }}). I would have expected that to work, but once you do setStore('signal', 'some-value') it seems to break the setter/getter.
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
bigmistqke
bigmistqke•3mo ago
aa nvm, that's not the best approach because then it would mean that once the array becomes large enough again it would jump back to the original cursor. you do want to reset the cursor-signal like this
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
gsoutz
gsoutzOP•3mo ago
In solidjs real world example here: https://github.com/solidjs/solid-realworld/blob/main/src/store/index.js#L18 The store has a articles getter (no setter) but still later can be set to other values. Also the getter tracks the changes so it can set itself reactively as well. Here's my example: https://playground.solidjs.com/anonymous/5ba79d4f-cd7f-470b-a8ef-cbf475aee207 In my example this throws an error when I try to set the property that has only a getter. Not sure If I am missing something but how does that work in the real world example, I am wondering.
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
GitHub
solid-realworld/src/store/index.js at main · solidjs/solid-realworld
A Solid Implementation of the Realworld Example App - solidjs/solid-realworld
gsoutz
gsoutzOP•3mo ago
Here's an example place where "articles" is set to custom value https://github.com/solidjs/solid-realworld/blob/main/src/store/createArticles.js#L45
GitHub
solid-realworld/src/store/createArticles.js at main · solidjs/soli...
A Solid Implementation of the Realworld Example App - solidjs/solid-realworld
bigmistqke
bigmistqke•3mo ago
oof that code is rough 😅 mutating that actions-object everywhere... pre-typescript mindsets i think the way this works is that in
setState("articles", slug, (s) => ({
favorited: true,
favoritesCount: s.favoritesCount + 1
}));
setState("articles", slug, (s) => ({
favorited: true,
favoritesCount: s.favoritesCount + 1
}));
you are not actually setting the store.articles but you are setting store.articles[slug]. but then when you look in what createArticle returns, store.articles is a resource. so not sure how that works. confusing codebase that realworld example
bigmistqke
bigmistqke•3mo ago
but for example something like this would work, where you nest stores into one another
const [cursor] = createStore({value: 0})
const [p_store, set_p_store] = createStore({
get array() {
return array();
},
get cursor() {
return cursor
},
});
set_p_store('cursor', 'value', (value) => value + 1)
const [cursor] = createStore({value: 0})
const [p_store, set_p_store] = createStore({
get array() {
return array();
},
get cursor() {
return cursor
},
});
set_p_store('cursor', 'value', (value) => value + 1)
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
zulu
zulu•3mo ago
I will add one more possible pattern
let [cursor, setCursor] = createWritableMemo((v: number) => {
return v >= my_array().length ? my_array().length - 1 : v;
}, 0);
let [cursor, setCursor] = createWritableMemo((v: number) => {
return v >= my_array().length ? my_array().length - 1 : v;
}, 0);
https://playground.solidjs.com/anonymous/95ffaec3-b169-406c-ba4b-c8d4b7e5758b
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
bigmistqke
bigmistqke•3mo ago
True. I wish we could mock createProjection too. Can not wait tbh. (createWritableMemo is how solid 2.0 createSignal will work)
gsoutz
gsoutzOP•3mo ago
"Everytime the array changes, cursor needs to reset to 0, then a few seconds later go to 1". It's like a preview of the 0 index then it transitions to show 1 index."
zulu
zulu•3mo ago
what does this mean?
gsoutz
gsoutzOP•3mo ago
That's what I wanted to do.
zulu
zulu•3mo ago
then you don't really need "sync". you want to detect array change. set the index to 0 and with a timer set it to 1 ?
gsoutz
gsoutzOP•3mo ago
yes, I can probably easily do that with a createEffect, but before the effect runs after the array changes some memos can show dangling pointers. That's why I mentioned "sync"
zulu
zulu•3mo ago
1. well probably not easily, you still need to consider the case the array changes again before the timer 2. if you look at my example, you can see that this memo is self guarding.
gsoutz
gsoutzOP•2mo ago
Ok I will try it thanks. Could you recommend me an example repo demonstrating async resources shared state and separation of actions vs state and things like that, like a real world example.
bigmistqke
bigmistqke•2mo ago
mm, no sorry, I don't really have any recommendations for u. @davedbase do u maybe have an idea?

Did you find this page helpful?