S
SolidJS•2y ago
.torx

How to preserve reactivity in a multilevel object stored in a nanostore map ?

Hello, I'm trying to create a simple cart store using the nanostores library (https://github.com/nanostores/solid), the data looks like this :
export const getCartItems = map<CartItems>({
id1: {
id: 'id1',
quantity: 1,
type: 'product',
meta: {
// Some static values ...
}
},
id2: {
id: 'id2',
quantity: 1,
type: 'product',
meta: {
// Some static values ...
}
},
})
export const getCartItems = map<CartItems>({
id1: {
id: 'id1',
quantity: 1,
type: 'product',
meta: {
// Some static values ...
}
},
id2: {
id: 'id2',
quantity: 1,
type: 'product',
meta: {
// Some static values ...
}
},
})
I'm reading the store like this :
const CartItem = () => {
const $items = useStore(getCartItems)
const $total = useStore(getCartTotal)

return (
<div>
<h1 class="text-3xl bold pb-6">Total : {$total()}</h1>

<div class="pb-2">
<h1>items </h1>
<For each={Object.values($items())}>
{(item) => (
<span><b>id</b>{item.id}, <b>quantity</b>: {item.quantity}</span>
)}
</For>
</div>
</div>
)
}
const CartItem = () => {
const $items = useStore(getCartItems)
const $total = useStore(getCartTotal)

return (
<div>
<h1 class="text-3xl bold pb-6">Total : {$total()}</h1>

<div class="pb-2">
<h1>items </h1>
<For each={Object.values($items())}>
{(item) => (
<span><b>id</b>{item.id}, <b>quantity</b>: {item.quantity}</span>
)}
</For>
</div>
</div>
)
}
I'm facing 2 problems :
1 - If I add a new item to the cart, it's reactive. If I remove an item or reset the cart, it's also reactive. If I try to increase the quantity of an item, it's NOT reactive. I think this is because it's a nested object, but how can I fix this to make it reactive without changing the nanostore library ? 2 - useStore returns a signal, but I'd like to use a useCart function that returns every cart-related logic (because I want it to be a library and it's annoying to install @nanostores/solid in the final project), I tryed different things, none of them work :
// don't work
const useCart = () => {
items: useStore(getCartItems)
}

// deriving signal, don't work either
const useCart = () => {
items: () => useStore(getCartItems)()
}

// Not working either withtout the object
const $getCartItems = () => useStore(getCartItems)()
// don't work
const useCart = () => {
items: useStore(getCartItems)
}

// deriving signal, don't work either
const useCart = () => {
items: () => useStore(getCartItems)()
}

// Not working either withtout the object
const $getCartItems = () => useStore(getCartItems)()
Is there a solution to preserve the reactivity without having to import the useStore function in the final project ? Thanks for the help!
25 Replies
Unknown User
Unknown User•2y ago
Message Not Public
Sign In & Join Server To View
.torx
.torxOP•2y ago
Thanks, 1) is solved! any idea about the 2) ?
Unknown User
Unknown User•2y ago
Message Not Public
Sign In & Join Server To View
.torx
.torxOP•2y ago
Reactivity is lost like the other exemples,
Unknown User
Unknown User•2y ago
Message Not Public
Sign In & Join Server To View
.torx
.torxOP•2y ago
(<div class="pb-2">
<h1>lib</h1>
<For each={Object.values(useCart().items())}>
{(item) => (
<span><b>id</b>{item.id}, <b>quantity</b>: {item.quantity}</span>
)}
</For>
</div>)
(<div class="pb-2">
<h1>lib</h1>
<For each={Object.values(useCart().items())}>
{(item) => (
<span><b>id</b>{item.id}, <b>quantity</b>: {item.quantity}</span>
)}
</For>
</div>)
Should I use the splitProps function ?
Unknown User
Unknown User•2y ago
Message Not Public
Sign In & Join Server To View
.torx
.torxOP•2y ago
I don't think the problem is from the .values(), otherwise using the useStore function wouldn't work either, it is like the useCart function makes solid lost the track of the signal.
const CartItem = () => {
const $items = useStore(getCartItems)
const $total = useStore(getCartTotal)

// It doesn't work either with useCart destructuring.
const [items, rest] = splitProps(useCart(), ['items'])

return (
<div>
<h1 class="text-3xl bold pb-6">{$total()}</h1>

// This doesn't works.
<div class="pb-2">
<h1>useCart Items</h1>

<For each={Object.values(items.items())}>
{(item) => (
<span><b>id</b>{item.id}, <b>quantity</b>: {item.quantity}</span>
)}
</For>

<p>{items.items()[1].quantity}</p>
</div>

// this works
<div class="pb-2">
<h1>useStore Items</h1>
<For each={Object.values($items())}>
{(item) => (
<span><b>id</b>{item.id}, <b>quantity</b>: {item.quantity}</span>
)}
</For>
</div>
</div>
)
}
const CartItem = () => {
const $items = useStore(getCartItems)
const $total = useStore(getCartTotal)

// It doesn't work either with useCart destructuring.
const [items, rest] = splitProps(useCart(), ['items'])

return (
<div>
<h1 class="text-3xl bold pb-6">{$total()}</h1>

// This doesn't works.
<div class="pb-2">
<h1>useCart Items</h1>

<For each={Object.values(items.items())}>
{(item) => (
<span><b>id</b>{item.id}, <b>quantity</b>: {item.quantity}</span>
)}
</For>

<p>{items.items()[1].quantity}</p>
</div>

// this works
<div class="pb-2">
<h1>useStore Items</h1>
<For each={Object.values($items())}>
{(item) => (
<span><b>id</b>{item.id}, <b>quantity</b>: {item.quantity}</span>
)}
</For>
</div>
</div>
)
}
I don't really understand why deriving the signal doesn't work in that situation, By "doesn't works" I mean it gets the initial value, but loose reactivity
Unknown User
Unknown User•2y ago
Message Not Public
Sign In & Join Server To View
.torx
.torxOP•2y ago
It doesn't work, (don't even gets the initial value)
Unknown User
Unknown User•2y ago
Message Not Public
Sign In & Join Server To View
.torx
.torxOP•2y ago
Same as the rest, I really think the problem is in the useCart function but I don't get what
mdynnl
mdynnl•2y ago
looking at their integration, i think you can just use the value of the signal returned by useStore, useStore(store)() is basically a reference to a nested store prop store.value which will have a stable reference as they just use setStore('value', reconcile(v)) hmm, more like store = { get value() { return value() } } is more appropriate if you use primitives
.torx
.torxOP•2y ago
Like that ?
export const useCart = () => {
return {
get items() { return useStore(getCartItems)}
}
}
export const useCart = () => {
return {
get items() { return useStore(getCartItems)}
}
}
mdynnl
mdynnl•2y ago
useStore should be top level in a component cause it's basically creats a solid store useStore returns a new solid store that gets synced to the nano store
.torx
.torxOP•2y ago
So, it's impossible to use a hook between useStore and a component ?
mdynnl
mdynnl•2y ago
what do you mean exactly? useStore is basically createStore underhood, it returns a signal to the nested store value, their integration is only a few lines though, you could just look at it and understand it better if you already know solid stores
.torx
.torxOP•2y ago
Let me recontextualize, I created nanostores in a library to handle some logic shared between every websites we develop library/core. This library is used by a second library that handle framework specificity : library/solidjs, library/react, etc. In my final project, I install "library/core" and library/solidjs, and Creates a component that need the cart store. The problem is, I must also install @nanostores/solid to read the store via useStore, which, you'r right, creates a solid store. When I use useStore inside my component, it works well, no problem about it. But, I don't want to tell people they will need to install @nanostores/solid to use the logic inside my library, I'd prefer to have a function in library/solidjs that would return the return value of useStore, but if I do it, I loose the reactivity.
mdynnl
mdynnl•2y ago
i see, then it's possibly a bundling issue, you could check if solid-js gets bundled into your library output
.torx
.torxOP•2y ago
My other solid related logic works, so it must be bundled
mdynnl
mdynnl•2y ago
i'm not sure if docs exists on building a solid library, but you need to exclude solid-js from bundling what are you using currently?
.torx
.torxOP•2y ago
I'm using this vite config :
module.exports = defineConfig({
build: {
lib: {
entry: path.resolve(__dirname, 'src/index.ts'),
name: 'solidjs',
fileName: 'solidjs'
}
},
plugins: [dts({
insertTypesEntry: true
}), solidPlugin()],
});
module.exports = defineConfig({
build: {
lib: {
entry: path.resolve(__dirname, 'src/index.ts'),
name: 'solidjs',
fileName: 'solidjs'
}
},
plugins: [dts({
insertTypesEntry: true
}), solidPlugin()],
});
It's my first non vanilla library, why should I exclude solid ?
mdynnl
mdynnl•2y ago
two instances of solid does not work together, i think there's even a dev warning if there's multiple instances
rollupOptions: {
external: ['solid-js', 'solid-js/web', 'solid-js/store']
}
rollupOptions: {
external: ['solid-js', 'solid-js/web', 'solid-js/store']
}
should work if you're not using j|tsx also https://github.com/solidjs-community/rollup-preset-solid exists which covers j|tsx too probably also add solid-js to peerDeps in package.json
.torx
.torxOP•2y ago
Ok, it fixed it Thank you so much 🙂
bigmistqke
bigmistqke•2y ago
https://github.com/solidjs-community/solid-lib-starter i use this as a starter when making solid libraries
GitHub
GitHub - solidjs-community/solid-lib-starter: SolidJS library start...
SolidJS library starter template. Use it to create your own solid package. - GitHub - solidjs-community/solid-lib-starter: SolidJS library starter template. Use it to create your own solid package.
Want results from more Discord servers?
Add your server