Accessing parts of a Store (sub store, substore)

I ran into some typing issue with stores. I've got a component that expects a data: T[] and setter: SetStoreFunction<T[]> . This is all fine when passing in a complete store as created by createStore(...) but breaks down if I want to pass a part around: e.g.
<Foo
data={store.foo}
setter={(...args) => setStore('foo', ...args)}
/>
<Foo
data={store.foo}
setter={(...args) => setStore('foo', ...args)}
/>
While this does indeed work at runtime TS goes bonkers about this as is doesn't know the type of args and I can't deduce anything due to the overload of the SetStoreFunction interface. fwiw. would be a helper like subStoreSetter(store, "foo") which returns that setter function so it could be used like that:
const setFoo = subStoreSetter(setStore, 'foo')
<Foo
data={store.foo}
setter={setFoo}
/>
const setFoo = subStoreSetter(setStore, 'foo')
<Foo
data={store.foo}
setter={setFoo}
/>
While we're at it that feature could also be a member of the setStore function which would look really nice API wise:
const setFoo = setStore.sub('foo')
const setFoo = setStore.sub('foo')
I don't know if this is even possible or if I should be replacing that one store object with multiple arrays into multiple stores one per array. Maybe that feature is still around somewhere and I'm just not seeing it. 🙈
5 Replies
Sarguel
Sarguel•3y ago
I have the same typescript situation:
const sum = (...theArgs: number[]): number => (theArgs.length > 0 ? theArgs[0] + sum(...theArgs.slice(1)) : 0)
const sumWithPrefix = (...rest_path: any[]) => sum(1, 2, 3, ...rest_path) // <- no error here

console.log(sum(0, 1, 2, 3, 4)) // [LOG]: 10
console.log(sumWithPrefix(0, 1, 2, 3, 4)) // [LOG]: 16

const [nums, setNums] = createStore([] as number [])
const testSetNums = (...rest_path: any[]) => setNums({ from: 2, to: 5 }, ...rest_path) // <- error "A spread argument must either have a tuple type or be passed to a rest parameter."

setNums([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
setNums(_ => true, n => -n)
console.log(`${nums}`) // [LOG]: 0,-1,-2,-3,-4,-5,-6,-7,-8,-9

setNums([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
testSetNums((n: number) => -n)
console.log(`${nums}`) // [LOG]: 0,1,-2,-3,-4,-5,6,7,8,9 ( It "works" )
const sum = (...theArgs: number[]): number => (theArgs.length > 0 ? theArgs[0] + sum(...theArgs.slice(1)) : 0)
const sumWithPrefix = (...rest_path: any[]) => sum(1, 2, 3, ...rest_path) // <- no error here

console.log(sum(0, 1, 2, 3, 4)) // [LOG]: 10
console.log(sumWithPrefix(0, 1, 2, 3, 4)) // [LOG]: 16

const [nums, setNums] = createStore([] as number [])
const testSetNums = (...rest_path: any[]) => setNums({ from: 2, to: 5 }, ...rest_path) // <- error "A spread argument must either have a tuple type or be passed to a rest parameter."

setNums([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
setNums(_ => true, n => -n)
console.log(`${nums}`) // [LOG]: 0,-1,-2,-3,-4,-5,-6,-7,-8,-9

setNums([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
testSetNums((n: number) => -n)
console.log(`${nums}`) // [LOG]: 0,1,-2,-3,-4,-5,6,7,8,9 ( It "works" )
my solution at the moment is : @ts-ignore but I probably missed something
Sarguel
Sarguel•3y ago
The solution you propose doesn't fit for me for 2 reasons: - This is mainly a typing issue ( again it work at runtime ), so solving it by instantiating a second store feel overkill - My use case is inside a For component, think todo app, where each sub component need to update itself in the main store, to create a substore for each of them feel realy overkill link to playground: https://playground.solidjs.com/anonymous/5924df47-7ae8-4103-82f6-017a76c87060
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Sarguel
Sarguel•3y ago
It is overkill in the sens that I'm using a solid api to solve a typing issue which doesn't feel good. The underlying reason as to why typescript is complaining in this case is because ...rest_path can be any size whereas a store setter function can have variable amount of argument only up to the max depth of your store.
Michael P. Jung
Michael P. JungOP•3y ago
@ryansolid Sorry for pinging you directly, but maybe we're missing an important point here. Are stores even meant to be used like that? Like components working only on parts of one bigger store or is this really not the idea of it. As a KO veteran I would've just used observables and observableArrays all the way down with a bunch of helper methods to keep the code clean. With SolidJS and the "new" store primitive I feel like I should be doing it a different way...
ryansolid
ryansolid•3y ago
I think in general this is a good pattern. You are explicitly sectioning it off. You could make seperate stores but where you want to pass a section down you have explicit control over how it reads and writes. Simple cases might be better to pass down specific actions for more control.. But nested setter seems good. We've been sort of trying to decide what the best pattern for Lenses would be.

Did you find this page helpful?