S
SolidJS•10mo ago
skrebbel

Derived data in a store vs reconcile

Hey there, I'm trying to somehow have derived/computed data sit in a rather deeply nested store, ie values that depend on "real" values stored in the store. The docs suggests getters, and that works fine, until I try to use reconcile:
const [state, setState] = createStore({
a: 5,
b: 6,
get c() { return state.a + state.b; }
});

setState(reconcile({ a: 7, b: 6 }));
console.log(state.c); //error, `c` got deleted
const [state, setState] = createStore({
a: 5,
b: 6,
get c() { return state.a + state.b; }
});

setState(reconcile({ a: 7, b: 6 }));
console.log(state.c); //error, `c` got deleted
Our data is a particularly great fit for reconcile, it'd be a shame to not be able to use that. In the end I found a workaround, which is to make a field data with the all the raw data in it (and reconcile that), and put the getters one level higher in the hierarchy. Is that the recommended approach? Or is there a different way to have reconcile leave the setters be? I noticed that with a class (returning createMutable(this) from the constructor) it actually works, ie I can put instances of that class in a store and reconcile on the fields, and the getters/methods aren't touched because they're on the prototype and not the instance. But I don't love it because, even though I really like classes/OO, I don't really love making them fully mutable for all the reasons Ryan likes to cite. I'd totally go for an immutable class, F# with style, but I've yet to find an elegant way to accomplish that with JS (let alone solid stores / reconcile). Any ideas? Thanks!
5 Replies
bigmistqke
bigmistqke•10mo ago
In the end I found a workaround, which is to make a field data with the all the raw data in it (and reconcile that), and put the getters one level higher in the hierarchy. Is that the recommended approach?
That's how I would do it too.
skrebbel
skrebbelOP•10mo ago
thanks 🙂 it hurts my OO-heart though 😿 I think that when our industry ditched OO for its mutable AbstractFactoryServiceProvider bathwater, we threw out the "couple data with operations on that data" baby. would be lovely IMO if caller code don't need to care whether something is raw data or a getter. also it means that if some code makes a mistake somewhere and reconciles the wrong level of the tree, all the getters are tossed. i recognize that reconcile is a crude tool not wielded lightly, but it still makes it all feel a little bit risky
bigmistqke
bigmistqke•10mo ago
an underdocumented feature is that stores are composable. you could use this to separate the reconciled store.
const externalData = {
count: 0,
};
const [store, setStore] = createStore({
get double() {
return this.data.count * 2;
},
data: { ...externalData },
});
const [data, setData] = createStore(store.data);

return (
<button
type="button"
onClick={() => {
externalData.count++;
setData(reconcile(externalData));
}}
>
{store.double}
</button>
);
const externalData = {
count: 0,
};
const [store, setStore] = createStore({
get double() {
return this.data.count * 2;
},
data: { ...externalData },
});
const [data, setData] = createStore(store.data);

return (
<button
type="button"
onClick={() => {
externalData.count++;
setData(reconcile(externalData));
}}
>
{store.double}
</button>
);
see https://playground.solidjs.com/anonymous/e9b4d649-81db-4f66-ac39-ec986f356c31
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
bigmistqke
bigmistqke•10mo ago
i'm not sure if i follow completely. imo reconcile behaves pretty transparent. when reconciling with the external data, i assume the derived data is not included, so it makes sense that it removes those properties when reconciling. relying on static properties or messing around with prototypes would make code less readable imo. you could also do
const [data, setData] = createStore(externalData)
const api = {
get double(){ return this.data.count * 2 },
data,
}
return (
<button
type="button"
onClick={() => {
externalData.count++;
setData(reconcile(externalData));
}}
>
{api.double}
</button>
);
const [data, setData] = createStore(externalData)
const api = {
get double(){ return this.data.count * 2 },
data,
}
return (
<button
type="button"
onClick={() => {
externalData.count++;
setData(reconcile(externalData));
}}
>
{api.double}
</button>
);
skrebbel
skrebbelOP•10mo ago
Right! This is very helpful, thanks!
Want results from more Discord servers?
Add your server